001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software GmbH & Co. KG, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.util; 029 030import org.opencms.ade.configuration.CmsADEConfigData; 031import org.opencms.configuration.CmsParameterStore; 032import org.opencms.db.CmsUserSettings; 033import org.opencms.file.CmsFile; 034import org.opencms.file.CmsObject; 035import org.opencms.file.CmsProperty; 036import org.opencms.file.CmsPropertyDefinition; 037import org.opencms.file.CmsRequestContext; 038import org.opencms.file.CmsResource; 039import org.opencms.file.CmsResourceFilter; 040import org.opencms.file.types.CmsResourceTypeBinary; 041import org.opencms.file.types.CmsResourceTypeImage; 042import org.opencms.flex.CmsFlexController; 043import org.opencms.i18n.CmsMessageContainer; 044import org.opencms.i18n.CmsMessages; 045import org.opencms.i18n.CmsMultiMessages; 046import org.opencms.main.CmsException; 047import org.opencms.main.CmsIllegalArgumentException; 048import org.opencms.main.CmsLog; 049import org.opencms.main.OpenCms; 050import org.opencms.report.I_CmsReport; 051import org.opencms.security.CmsOrganizationalUnit; 052import org.opencms.ui.apps.sitemanager.CmsSiteManager; 053import org.opencms.ui.editors.messagebundle.CmsMessageBundleEditorTypes.Descriptor; 054import org.opencms.xml.CmsXmlException; 055import org.opencms.xml.content.CmsXmlContent; 056import org.opencms.xml.content.CmsXmlContentFactory; 057import org.opencms.xml.content.CmsXmlContentValueSequence; 058 059import java.io.UnsupportedEncodingException; 060import java.nio.charset.Charset; 061import java.util.Arrays; 062import java.util.Collections; 063import java.util.HashMap; 064import java.util.Iterator; 065import java.util.LinkedHashMap; 066import java.util.List; 067import java.util.Map; 068import java.util.Properties; 069 070import javax.servlet.jsp.PageContext; 071 072import org.apache.commons.collections.Factory; 073import org.apache.commons.logging.Log; 074 075import com.google.common.base.Function; 076 077/** 078 * Resolves macros in the form of <code>%(key)</code> or <code>${key}</code> in an input String.<p> 079 * 080 * Starting with OpenCms 7.0, the preferred form of a macro is <code>%(key)</code>. This is to 081 * avoid conflicts / confusion with the JSP EL, which also uses the <code>${key}</code> syntax.<p> 082 * 083 * The macro names that can be resolved depend of the context objects provided to the resolver 084 * using the <code>set...</code> methods.<p> 085 * 086 * @since 6.0.0 087 */ 088public class CmsMacroResolver implements I_CmsMacroResolver { 089 090 /** The prefix indicating that the key represents an OpenCms runtime attribute. */ 091 public static final String KEY_ATTRIBUTE = "attribute."; 092 093 /** Key used to specify the context path as macro value. */ 094 public static final String KEY_CONTEXT_PATH = "contextPath"; 095 096 /** Key used to specify the description of the current organizational unit as macro value. */ 097 public static final String KEY_CURRENT_ORGUNIT_DESCRIPTION = "currentou.description"; 098 099 /** Key used to specify the full qualified name of the current organizational unit as macro value. */ 100 public static final String KEY_CURRENT_ORGUNIT_FQN = "currentou.fqn"; 101 102 /** Key used to specify the current time as macro value. */ 103 public static final String KEY_CURRENT_TIME = "currenttime"; 104 105 /** Key used to specify the city of the current user as macro value. */ 106 public static final String KEY_CURRENT_USER_CITY = "currentuser.city"; 107 108 /** Key used to specify the country of the current user as macro value. */ 109 public static final String KEY_CURRENT_USER_COUNTRY = "currentuser.country"; 110 111 /** Key used to specify the display name of the current user as macro value. */ 112 public static final String KEY_CURRENT_USER_DISPLAYNAME = "currentuser.displayname"; 113 114 /** Key used to specify the email address of the current user as macro value. */ 115 public static final String KEY_CURRENT_USER_EMAIL = "currentuser.email"; 116 117 /** Key used to specify the first name of the current user as macro value. */ 118 public static final String KEY_CURRENT_USER_FIRSTNAME = "currentuser.firstname"; 119 120 /** Key used to specify the full name of the current user as macro value. */ 121 public static final String KEY_CURRENT_USER_FULLNAME = "currentuser.fullname"; 122 123 /** Key used to specify the institution of the current user as macro value. */ 124 public static final String KEY_CURRENT_USER_INSTITUTION = "currentuser.institution"; 125 126 /** Key used to specify the last login date of the current user as macro value. */ 127 public static final String KEY_CURRENT_USER_LASTLOGIN = "currentuser.lastlogin"; 128 129 /** Key used to specify the last name of the current user as macro value. */ 130 public static final String KEY_CURRENT_USER_LASTNAME = "currentuser.lastname"; 131 132 /** Key used to specify the user name of the current user as macro value. */ 133 public static final String KEY_CURRENT_USER_NAME = "currentuser.name"; 134 135 /** Key used to specify the street of the current user as macro value. */ 136 public static final String KEY_CURRENT_USER_STREET = "currentuser.street"; 137 138 /** Key used to specify the zip code of the current user as macro value. */ 139 public static final String KEY_CURRENT_USER_ZIP = "currentuser.zip"; 140 141 /** Key prefix used to specify the value of a localized key as macro value. */ 142 public static final String KEY_LOCALIZED_PREFIX = "key."; 143 144 /** Identifier for "magic" parameter names. */ 145 public static final String KEY_OPENCMS = "opencms."; 146 147 /** The prefix indicating that the key represents a page context object. */ 148 public static final String KEY_PAGE_CONTEXT = "pageContext."; 149 150 /** Prefix for getting parameters from the CmsParameterStore. */ 151 public static final String KEY_PARAM = "param:"; 152 153 /** Key used to specify the project id as macro value. */ 154 public static final String KEY_PROJECT_ID = "projectid"; 155 156 /** The prefix indicating that the key represents a property to be read on the current request URI. */ 157 public static final String KEY_PROPERTY = "property."; 158 159 /** The prefix indicating that the key represents a property to be read on the current element. */ 160 public static final String KEY_PROPERTY_ELEMENT = "elementProperty."; 161 162 /** Key used to specify a random id as macro value. */ 163 public static final String KEY_RANDOM_ID = "randomId"; 164 165 /** Key used to specify the request encoding as macro value. */ 166 public static final String KEY_REQUEST_ENCODING = "request.encoding"; 167 168 /** Key used to specify the folder of the request URI as macro value. */ 169 public static final String KEY_REQUEST_FOLDER = "request.folder"; 170 171 /** Key user to specify the request locale as macro value. */ 172 public static final String KEY_REQUEST_LOCALE = "request.locale"; 173 174 /** The prefix indicating that the key represents a HTTP request parameter. */ 175 public static final String KEY_REQUEST_PARAM = "param."; 176 177 /** Key used to specify the request site root as macro value. */ 178 public static final String KEY_REQUEST_SITEROOT = "request.siteroot"; 179 180 /** Key used to specify the request uri as macro value. */ 181 public static final String KEY_REQUEST_URI = "request.uri"; 182 183 /** Key used to specify the validation path as macro value. */ 184 public static final String KEY_VALIDATION_PATH = "validation.path"; 185 186 /** Key used to specify the validation regex as macro value. */ 187 public static final String KEY_VALIDATION_REGEX = "validation.regex"; 188 189 /** Key used to specify the validation value as macro value. */ 190 public static final String KEY_VALIDATION_VALUE = "validation.value"; 191 192 /** Key for accessing sitemap attributes. */ 193 public static final String KEY_SITEMAP_ATTRIBUTE = "attribute:"; 194 195 /** Identified for "magic" parameter commands. */ 196 static final String[] VALUE_NAMES_ARRAY = { 197 "uri", // 0 198 "filename", // 1 199 "folder", // 2 200 "default.encoding", // 3 201 "remoteaddress", // 4 202 "webapp", // 5 203 "webbasepath", // 6 204 "version", // 7 205 "versionid" // 8 206 }; 207 208 /** The "magic" commands wrapped in a List. */ 209 public static final List<String> VALUE_NAMES = Collections.unmodifiableList(Arrays.asList(VALUE_NAMES_ARRAY)); 210 211 /** The log object for this class. */ 212 private static final Log LOG = CmsLog.getLog(CmsMacroResolver.class); 213 214 /** A map of additional values provided by the calling class. */ 215 protected Map<String, String> m_additionalMacros; 216 217 /** The OpenCms user context to use for resolving macros. */ 218 protected CmsObject m_cms; 219 220 /** The JSP's page context to use for resolving macros. */ 221 protected PageContext m_jspPageContext; 222 223 /** Indicates if unresolved macros should be kept "as is" or replaced by an empty String. */ 224 protected boolean m_keepEmptyMacros; 225 226 /** The messages resource bundle to resolve localized keys with. */ 227 protected CmsMessages m_messages; 228 229 /** The request parameter map, used for better compatibility with multi part requests. */ 230 protected Map<String, String[]> m_parameterMap; 231 232 /** The resource name to use for resolving macros. */ 233 protected String m_resourceName; 234 235 /** A map from names of dynamic macros to the factories which generate their values. */ 236 private Map<String, Factory> m_factories; 237 238 /** 239 * Copies resources, adjust internal links (if adjustLinks==true) and resolves macros (if keyValue map is set).<p> 240 * 241 * @param cms CmsObject 242 * @param source path 243 * @param destination path 244 * @param keyValue map to be used for macro resolver 245 * @param adjustLinks boolean, true means internal links get adjusted. 246 * @throws CmsException exception 247 */ 248 public static void copyAndResolveMacro( 249 CmsObject cms, 250 String source, 251 String destination, 252 Map<String, String> keyValue, 253 boolean adjustLinks) 254 throws CmsException { 255 256 copyAndResolveMacro(cms, source, destination, keyValue, adjustLinks, CmsResource.COPY_AS_NEW); 257 } 258 259 /** 260 * Copies resources, adjust internal links (if adjustLinks==true) and resolves macros (if keyValue map is set).<p> 261 * 262 * @param cms CmsObject 263 * @param source path 264 * @param destination path 265 * @param keyValue map to be used for macro resolver 266 * @param adjustLinks boolean, true means internal links get adjusted. 267 * @param copyMode copyMode 268 * @throws CmsException exception 269 */ 270 271 public static void copyAndResolveMacro( 272 CmsObject cms, 273 String source, 274 String destination, 275 Map<String, String> keyValue, 276 boolean adjustLinks, 277 CmsResource.CmsResourceCopyMode copyMode) 278 throws CmsException { 279 280 copyAndResolveMacro(cms, source, destination, keyValue, adjustLinks, copyMode, null); 281 282 } 283 284 /** 285 * Copies resources, adjust internal links (if adjustLinks==true) and resolves macros (if keyValue map is set).<p> 286 * 287 * @param cms CmsObject 288 * @param source path 289 * @param destination path 290 * @param keyValue map to be used for macro resolver 291 * @param adjustLinks boolean, true means internal links get adjusted. 292 * @param copyMode copy Mode 293 * @param report report to write logs to 294 * @throws CmsException exception 295 */ 296 public static void copyAndResolveMacro( 297 CmsObject cms, 298 String source, 299 String destination, 300 Map<String, String> keyValue, 301 boolean adjustLinks, 302 CmsResource.CmsResourceCopyMode copyMode, 303 I_CmsReport report) 304 throws CmsException { 305 306 if (report != null) { 307 report.println( 308 org.opencms.ui.apps.Messages.get().container( 309 org.opencms.ui.apps.Messages.RPT_MACRORESOLVER_COPY_RESOURCES_1, 310 source)); 311 } 312 cms.copyResource(source, destination, copyMode); 313 if (report != null) { 314 report.println( 315 org.opencms.ui.apps.Messages.get().container( 316 org.opencms.ui.apps.Messages.RPT_MACRORESOLVER_LINK_ADJUST_0)); 317 } 318 if (adjustLinks) { 319 cms.adjustLinks(source, destination); 320 } 321 //Guards to check if keyValue is set correctly, otherwise no adjustment is done 322 if (keyValue == null) { 323 return; 324 } 325 if (keyValue.isEmpty()) { 326 return; 327 } 328 329 if (report != null) { 330 report.println( 331 org.opencms.ui.apps.Messages.get().container( 332 org.opencms.ui.apps.Messages.RPT_MACRORESOLVER_APPLY_MACROS_0)); 333 } 334 335 CmsMacroResolver macroResolver = new CmsMacroResolver(); 336 macroResolver.setKeepEmptyMacros(true); 337 for (String key : keyValue.keySet()) { 338 339 macroResolver.addMacro(key, keyValue.get(key)); 340 } 341 342 //Collect all resources to loop over 343 List<CmsResource> resoucesToCopy = cms.readResources(destination, CmsResourceFilter.ALL, true); 344 for (CmsResource resource : resoucesToCopy) { 345 if (resource.isFile() 346 && (resource.getTypeId() != CmsResourceTypeBinary.getStaticTypeId()) 347 && (resource.getTypeId() != CmsResourceTypeImage.getStaticTypeId())) { 348 CmsFile file = cms.readFile(resource); 349 CmsMacroResolver.updateFile(cms, file, macroResolver); 350 } 351 CmsMacroResolver.updateProperties(cms, resource, macroResolver); 352 } 353 354 // apply macro to the folder itself 355 CmsResource resource = cms.readResource(destination, CmsResourceFilter.ALL); 356 357 CmsMacroResolver.updateProperties(cms, resource, macroResolver); 358 359 if (cms.existsResource(ensureFoldername(destination) + CmsSiteManager.MACRO_FOLDER)) { 360 cms.deleteResource( 361 ensureFoldername(destination) + CmsSiteManager.MACRO_FOLDER, 362 CmsResource.CmsResourceDeleteMode.valueOf(-1)); 363 } 364 365 } 366 367 /** 368 * Adds macro delimiters to the given input, 369 * for example <code>key</code> becomes <code>%(key)</code>.<p> 370 * 371 * @param input the input to format as a macro 372 * 373 * @return the input formatted as a macro 374 */ 375 public static String formatMacro(String input) { 376 377 StringBuffer result = new StringBuffer(input.length() + 4); 378 result.append(I_CmsMacroResolver.MACRO_DELIMITER); 379 result.append(I_CmsMacroResolver.MACRO_START); 380 result.append(input); 381 result.append(I_CmsMacroResolver.MACRO_END); 382 return result.toString(); 383 } 384 385 /** 386 * Reads a bundle (key, value, descriptor) from Descriptor Resource and property resource.<p> 387 * 388 * @param resourceBundle property resource 389 * @param descriptor resource 390 * @param clonedCms cms instance 391 * @return Map <key, [value, descriptor]> 392 * @throws CmsXmlException exception 393 * @throws CmsException exception 394 */ 395 public static Map<String, String[]> getBundleMapFromResources( 396 Properties resourceBundle, 397 CmsResource descriptor, 398 CmsObject clonedCms) 399 throws CmsXmlException, CmsException { 400 401 Map<String, String[]> ret = new LinkedHashMap<String, String[]>(); 402 403 //Read XML content of descriptor 404 CmsXmlContent xmlContentDesc = CmsXmlContentFactory.unmarshal(clonedCms, clonedCms.readFile(descriptor)); 405 CmsXmlContentValueSequence messages = xmlContentDesc.getValueSequence(Descriptor.N_MESSAGE, Descriptor.LOCALE); 406 407 //Iterate through content 408 for (int i = 0; i < messages.getElementCount(); i++) { 409 410 //Read key and default text from descriptor, label from bundle (localized) 411 String prefix = messages.getValue(i).getPath() + "/"; 412 String key = xmlContentDesc.getValue(prefix + Descriptor.N_KEY, Descriptor.LOCALE).getStringValue( 413 clonedCms); 414 String label = resourceBundle.getProperty(key); 415 String defaultText = xmlContentDesc.getValue( 416 prefix + Descriptor.N_DESCRIPTION, 417 Descriptor.LOCALE).getStringValue(clonedCms); 418 419 ret.put(key, new String[] {label, defaultText}); 420 } 421 return ret; 422 } 423 424 /** 425 * Returns <code>true</code> if the given input String if formatted like a macro, 426 * that is it starts with <code>{@link I_CmsMacroResolver#MACRO_DELIMITER_OLD} + 427 * {@link I_CmsMacroResolver#MACRO_START_OLD}</code> and ends with 428 * <code>{@link I_CmsMacroResolver#MACRO_END_OLD}</code>.<p> 429 * 430 * @param input the input to check for a macro 431 * @return <code>true</code> if the given input String if formatted like a macro 432 */ 433 public static boolean isMacro(String input) { 434 435 if (CmsStringUtil.isEmpty(input) || (input.length() < 3)) { 436 return false; 437 } 438 439 return (((input.charAt(0) == I_CmsMacroResolver.MACRO_DELIMITER_OLD) 440 && ((input.charAt(1) == I_CmsMacroResolver.MACRO_START_OLD) 441 && (input.charAt(input.length() - 1) == I_CmsMacroResolver.MACRO_END_OLD))) 442 || ((input.charAt(0) == I_CmsMacroResolver.MACRO_DELIMITER) 443 && ((input.charAt(1) == I_CmsMacroResolver.MACRO_START) 444 && (input.charAt(input.length() - 1) == I_CmsMacroResolver.MACRO_END)))); 445 } 446 447 /** 448 * Returns <code>true</code> if the given input String is a macro equal to the given macro name.<p> 449 * 450 * @param input the input to check for a macro 451 * @param macroName the macro name to check for 452 * 453 * @return <code>true</code> if the given input String is a macro equal to the given macro name 454 */ 455 public static boolean isMacro(String input, String macroName) { 456 457 if (isMacro(input)) { 458 return input.substring(2, input.length() - 1).equals(macroName); 459 } 460 return false; 461 } 462 463 /** 464 * Returns a macro for the given localization key with the given parameters.<p> 465 * 466 * @param keyName the name of the localized key 467 * @param params the optional parameter array 468 * 469 * @return a macro for the given localization key with the given parameters 470 */ 471 public static String localizedKeyMacro(String keyName, Object[] params) { 472 473 String parameters = ""; 474 if ((params != null) && (params.length > 0)) { 475 for (int i = 0; i < params.length; i++) { 476 if (params[i] != null) { 477 parameters += "|" + params[i].toString(); 478 } 479 } 480 } 481 return "" 482 + I_CmsMacroResolver.MACRO_DELIMITER 483 + I_CmsMacroResolver.MACRO_START 484 + CmsMacroResolver.KEY_LOCALIZED_PREFIX 485 + keyName 486 + parameters 487 + I_CmsMacroResolver.MACRO_END; 488 } 489 490 /** 491 * Factory method to create a new {@link CmsMacroResolver} instance.<p> 492 * 493 * @return a new instance of a {@link CmsMacroResolver} 494 */ 495 public static CmsMacroResolver newInstance() { 496 497 return new CmsMacroResolver(); 498 } 499 500 /** Returns a new macro resolver that loads message keys from the workplace bundle in the user setting's language. 501 * @param cms the CmsObject. 502 * @return a new macro resolver with messages from the workplace bundle in the current users locale. 503 */ 504 public static I_CmsMacroResolver newWorkplaceLocaleResolver(final CmsObject cms) { 505 506 // Resolve macros in the property configuration 507 CmsMacroResolver resolver = new CmsMacroResolver(); 508 resolver.setCmsObject(cms); 509 CmsUserSettings userSettings = new CmsUserSettings(cms.getRequestContext().getCurrentUser()); 510 CmsMultiMessages multimessages = new CmsMultiMessages(userSettings.getLocale()); 511 multimessages.addMessages(OpenCms.getWorkplaceManager().getMessages(userSettings.getLocale())); 512 resolver.setMessages(multimessages); 513 resolver.setKeepEmptyMacros(true); 514 515 return resolver; 516 } 517 518 /** 519 * Resolves the macros in the given input using the provided parameters.<p> 520 * 521 * A macro in the form <code>%(key)</code> or <code>${key}</code> in the content is replaced with it's assigned value 522 * returned by the <code>{@link I_CmsMacroResolver#getMacroValue(String)}</code> method of the given 523 * <code>{@link I_CmsMacroResolver}</code> instance.<p> 524 * 525 * If a macro is found that can not be mapped to a value by the given macro resolver, 526 * it is left untouched in the input.<p> 527 * 528 * @param input the input in which to resolve the macros 529 * @param cms the OpenCms user context to use when resolving macros 530 * @param messages the message resource bundle to use when resolving macros 531 * 532 * @return the input with the macros resolved 533 */ 534 public static String resolveMacros(String input, CmsObject cms, CmsMessages messages) { 535 536 CmsMacroResolver resolver = new CmsMacroResolver(); 537 resolver.m_cms = cms; 538 resolver.m_messages = messages; 539 resolver.m_keepEmptyMacros = true; 540 return resolver.resolveMacros(input); 541 } 542 543 /** 544 * Resolves macros in the provided input String using the given macro resolver.<p> 545 * 546 * A macro in the form <code>%(key)</code> or <code>${key}</code> in the content is replaced with it's assigned value 547 * returned by the <code>{@link I_CmsMacroResolver#getMacroValue(String)}</code> method of the given 548 * <code>{@link I_CmsMacroResolver}</code> instance.<p> 549 * 550 * If a macro is found that can not be mapped to a value by the given macro resolver, 551 * <code>{@link I_CmsMacroResolver#isKeepEmptyMacros()}</code> controls if the macro is replaced by 552 * an empty String, or is left untouched in the input.<p> 553 * 554 * @param input the input in which to resolve the macros 555 * @param resolver the macro resolver to use 556 * 557 * @return the input with all macros resolved 558 */ 559 public static String resolveMacros(final String input, I_CmsMacroResolver resolver) { 560 561 if ((input == null) || (input.length() < 3)) { 562 // macro must have at last 3 chars "${}" or "%()" 563 return input; 564 } 565 566 int pn = input.indexOf(I_CmsMacroResolver.MACRO_DELIMITER); 567 int po = input.indexOf(I_CmsMacroResolver.MACRO_DELIMITER_OLD); 568 569 if ((po == -1) && (pn == -1)) { 570 // no macro delimiter found in input 571 return input; 572 } 573 574 int len = input.length(); 575 StringBuffer result = new StringBuffer(len << 1); 576 int np, pp1, pp2, e; 577 String macro, value; 578 boolean keep = resolver.isKeepEmptyMacros(); 579 boolean resolvedNone = true; 580 char ds, de; 581 int p; 582 583 if ((po == -1) || ((pn > -1) && (pn < po))) { 584 p = pn; 585 ds = I_CmsMacroResolver.MACRO_START; 586 de = I_CmsMacroResolver.MACRO_END; 587 } else { 588 p = po; 589 ds = I_CmsMacroResolver.MACRO_START_OLD; 590 de = I_CmsMacroResolver.MACRO_END_OLD; 591 } 592 593 // append chars before the first delimiter found 594 result.append(input.substring(0, p)); 595 do { 596 pp1 = p + 1; 597 pp2 = pp1 + 1; 598 if (pp2 >= len) { 599 // remaining chars can't be a macro (minimum size is 3) 600 result.append(input.substring(p, len)); 601 break; 602 } 603 // get the next macro delimiter 604 if ((pn > -1) && (pn < pp1)) { 605 pn = input.indexOf(I_CmsMacroResolver.MACRO_DELIMITER, pp1); 606 } 607 if ((po > -1) && (po < pp1)) { 608 po = input.indexOf(I_CmsMacroResolver.MACRO_DELIMITER_OLD, pp1); 609 } 610 if ((po == -1) && (pn == -1)) { 611 // none found, make sure remaining chars in this segment are appended 612 np = len; 613 } else { 614 // check if the next delimiter is old or new style 615 if ((po == -1) || ((pn > -1) && (pn < po))) { 616 np = pn; 617 } else { 618 np = po; 619 } 620 } 621 // check if the next char is a "macro start" 622 char st = input.charAt(pp1); 623 if (st == ds) { 624 // we have a starting macro sequence "${" or "%(", now check if this segment contains a "}" or ")" 625 e = input.indexOf(de, p); 626 if ((e > 0) && (e < np)) { 627 // this segment contains a closing macro delimiter "}" or "]", so we may have found a macro 628 macro = input.substring(pp2, e); 629 // resolve macro 630 value = resolver.getMacroValue(macro); 631 e++; 632 if (value != null) { 633 // macro was successfully resolved 634 result.append(value); 635 resolvedNone = false; 636 } else if (keep) { 637 // macro was unknown, but should be kept 638 result.append(input.substring(p, e)); 639 } 640 } else { 641 // no complete macro "${...}" or "%(...)" in this segment 642 e = p; 643 } 644 } else { 645 // no macro start char after the "$" or "%" 646 e = p; 647 } 648 // set macro style for next delimiter found 649 if (np == pn) { 650 ds = I_CmsMacroResolver.MACRO_START; 651 de = I_CmsMacroResolver.MACRO_END; 652 } else { 653 ds = I_CmsMacroResolver.MACRO_START_OLD; 654 de = I_CmsMacroResolver.MACRO_END_OLD; 655 } 656 // append the remaining chars after the macro to the start of the next macro 657 result.append(input.substring(e, np)); 658 // this is a nerdy joke ;-) 659 p = np; 660 } while (p < len); 661 662 if (resolvedNone && keep) { 663 // nothing was resolved and macros should be kept, return original input 664 return input; 665 } 666 667 // input was changed during resolving of macros 668 return result.toString(); 669 } 670 671 /** 672 * Strips the macro delimiters from the given input, 673 * for example <code>%(key)</code> or <code>${key}</code> becomes <code>key</code>.<p> 674 * 675 * In case the input is not a macro, <code>null</code> is returned.<p> 676 * 677 * @param input the input to strip 678 * 679 * @return the macro stripped from the input, or <code>null</code> 680 */ 681 public static String stripMacro(String input) { 682 683 if (isMacro(input)) { 684 return input.substring(2, input.length() - 1); 685 } 686 return null; 687 } 688 689 /** 690 * Checks if there are at least one character in the folder name, 691 * also ensures that it ends with a '/' and doesn't start with '/'.<p> 692 * 693 * @param resourcename folder name to check (complete path) 694 * @return the validated folder name 695 * @throws CmsIllegalArgumentException if the folder name is empty or <code>null</code> 696 */ 697 private static String ensureFoldername(String resourcename) throws CmsIllegalArgumentException { 698 699 if (CmsStringUtil.isEmpty(resourcename)) { 700 throw new CmsIllegalArgumentException( 701 org.opencms.db.Messages.get().container(org.opencms.db.Messages.ERR_BAD_RESOURCENAME_1, resourcename)); 702 } 703 if (!CmsResource.isFolder(resourcename)) { 704 resourcename = resourcename.concat("/"); 705 } 706 if (resourcename.charAt(0) == '/') { 707 resourcename = resourcename.substring(1); 708 } 709 return resourcename; 710 } 711 712 /** 713 * Updates a single file with the given macro resolver.<p> 714 * 715 * @param cms the cms context 716 * @param file the file to update 717 * @param macroResolver the macro resolver to update with 718 * 719 * @throws CmsException if something goes wrong 720 */ 721 private static void updateFile(CmsObject cms, CmsFile file, CmsMacroResolver macroResolver) throws CmsException { 722 723 String encoding = cms.readPropertyObject(file, CmsPropertyDefinition.PROPERTY_CONTENT_ENCODING, true).getValue( 724 OpenCms.getSystemInfo().getDefaultEncoding()); 725 String content; 726 try { 727 content = macroResolver.resolveMacros(new String(file.getContents(), encoding)); 728 } catch (UnsupportedEncodingException e) { 729 try { 730 content = macroResolver.resolveMacros( 731 new String(file.getContents(), Charset.defaultCharset().toString())); 732 } catch (UnsupportedEncodingException e1) { 733 content = macroResolver.resolveMacros(new String(file.getContents())); 734 } 735 } 736 // update the content 737 try { 738 file.setContents(content.getBytes(encoding)); 739 } catch (UnsupportedEncodingException e) { 740 try { 741 file.setContents(content.getBytes(Charset.defaultCharset().toString())); 742 } catch (UnsupportedEncodingException e1) { 743 file.setContents(content.getBytes()); 744 } 745 } 746 // write the target file 747 cms.getRequestContext().setAttribute(CmsXmlContent.AUTO_CORRECTION_ATTRIBUTE, Boolean.TRUE); 748 try { 749 cms.writeFile(file); 750 } finally { 751 cms.getRequestContext().removeAttribute(CmsXmlContent.AUTO_CORRECTION_ATTRIBUTE); 752 } 753 } 754 755 /** 756 * Updates all properties of the given resource with the given macro resolver.<p> 757 * 758 * @param cms the cms context 759 * @param resource the resource to update the properties for 760 * @param macroResolver the macro resolver to use 761 * 762 * @throws CmsException if something goes wrong 763 */ 764 private static void updateProperties(CmsObject cms, CmsResource resource, CmsMacroResolver macroResolver) 765 throws CmsException { 766 767 Iterator<CmsProperty> it = cms.readPropertyObjects(resource, false).iterator(); 768 while (it.hasNext()) { 769 CmsProperty property = it.next(); 770 String resValue = null; 771 if (property.getResourceValue() != null) { 772 resValue = macroResolver.resolveMacros(property.getResourceValue()); 773 } 774 String strValue = null; 775 if (property.getStructureValue() != null) { 776 strValue = macroResolver.resolveMacros(property.getStructureValue()); 777 } 778 CmsProperty newProperty = new CmsProperty(property.getName(), strValue, resValue); 779 cms.writePropertyObject(cms.getSitePath(resource), newProperty); 780 } 781 } 782 783 /** 784 * Adds a macro whose value will be dynamically generated at macro resolution time.<p> 785 * 786 * The value will be generated for each occurence of the macro in a string.<p> 787 * 788 * @param name the name of the macro 789 * @param factory the macro value generator 790 */ 791 public void addDynamicMacro(String name, Factory factory) { 792 793 if (m_factories == null) { 794 m_factories = new HashMap<String, Factory>(); 795 } 796 m_factories.put(name, factory); 797 } 798 799 /** 800 * Adds a customized macro to this macro resolver.<p> 801 * 802 * @param key the macro to add 803 * @param value the value to return if the macro is encountered 804 */ 805 public void addMacro(String key, String value) { 806 807 if (m_additionalMacros == null) { 808 // use lazy initializing 809 m_additionalMacros = new HashMap<String, String>(); 810 } 811 m_additionalMacros.put(key, value); 812 } 813 814 /** 815 * @see org.opencms.util.I_CmsMacroResolver#getMacroValue(java.lang.String) 816 */ 817 public String getMacroValue(String macro) { 818 819 if (m_messages != null) { 820 if (macro.startsWith(CmsMacroResolver.KEY_LOCALIZED_PREFIX)) { 821 String keyName = macro.substring(CmsMacroResolver.KEY_LOCALIZED_PREFIX.length()); 822 return m_messages.keyWithParams(keyName); 823 } 824 } 825 826 if (m_factories != null) { 827 Factory factory = m_factories.get(macro); 828 if (factory != null) { 829 String value = (String)factory.create(); 830 return value; 831 } 832 } 833 834 if (m_jspPageContext != null) { 835 836 if (m_jspPageContext.getRequest() != null) { 837 if (macro.startsWith(CmsMacroResolver.KEY_REQUEST_PARAM)) { 838 // the key is a request parameter 839 macro = macro.substring(CmsMacroResolver.KEY_REQUEST_PARAM.length()); 840 String result = null; 841 if (m_parameterMap != null) { 842 String[] param = m_parameterMap.get(macro); 843 if ((param != null) && (param.length >= 1)) { 844 result = param[0]; 845 } 846 } else { 847 result = m_jspPageContext.getRequest().getParameter(macro); 848 } 849 if ((result == null) && macro.equals(KEY_PROJECT_ID)) { 850 result = m_cms.getRequestContext().getCurrentProject().getUuid().toString(); 851 } 852 return result; 853 } 854 855 if ((m_cms != null) && macro.startsWith(CmsMacroResolver.KEY_PROPERTY_ELEMENT)) { 856 857 // the key is a cms property to be read on the current element 858 859 macro = macro.substring(CmsMacroResolver.KEY_PROPERTY_ELEMENT.length()); 860 CmsFlexController controller = CmsFlexController.getController(m_jspPageContext.getRequest()); 861 try { 862 CmsProperty property = m_cms.readPropertyObject( 863 controller.getCurrentRequest().getElementUri(), 864 macro, 865 false); 866 if (property != CmsProperty.getNullProperty()) { 867 return property.getValue(); 868 } 869 } catch (CmsException e) { 870 if (LOG.isWarnEnabled()) { 871 LOG.warn( 872 Messages.get().getBundle().key( 873 Messages.LOG_PROPERTY_READING_FAILED_2, 874 macro, 875 controller.getCurrentRequest().getElementUri()), 876 e); 877 } 878 } 879 } 880 } 881 882 if (macro.startsWith(CmsMacroResolver.KEY_PAGE_CONTEXT)) { 883 // the key is a page context object 884 macro = macro.substring(CmsMacroResolver.KEY_PAGE_CONTEXT.length()); 885 int scope = m_jspPageContext.getAttributesScope(macro); 886 return m_jspPageContext.getAttribute(macro, scope).toString(); 887 } 888 } 889 890 if (m_cms != null) { 891 892 if (macro.startsWith(CmsMacroResolver.KEY_PROPERTY)) { 893 // the key is a cms property to be read on the current request URI 894 macro = macro.substring(CmsMacroResolver.KEY_PROPERTY.length()); 895 try { 896 CmsProperty property = m_cms.readPropertyObject(m_cms.getRequestContext().getUri(), macro, true); 897 if (property != CmsProperty.getNullProperty()) { 898 return property.getValue(); 899 } 900 } catch (CmsException e) { 901 if (LOG.isWarnEnabled()) { 902 CmsMessageContainer message = Messages.get().container( 903 Messages.LOG_PROPERTY_READING_FAILED_2, 904 macro, 905 m_cms.getRequestContext().getUri()); 906 LOG.warn(message.key(), e); 907 } 908 } 909 return null; 910 } 911 912 if (macro.startsWith(CmsMacroResolver.KEY_ATTRIBUTE)) { 913 // the key is an OpenCms runtime attribute 914 macro = macro.substring(CmsMacroResolver.KEY_ATTRIBUTE.length()); 915 Object attribute = m_cms.getRequestContext().getAttribute(macro); 916 if (attribute != null) { 917 return attribute.toString(); 918 } 919 return null; 920 } 921 922 if (macro.startsWith(CmsMacroResolver.KEY_OPENCMS)) { 923 924 // the key is a shortcut for a cms runtime value 925 926 String originalKey = macro; 927 macro = macro.substring(CmsMacroResolver.KEY_OPENCMS.length()); 928 int index = VALUE_NAMES.indexOf(macro); 929 String value = null; 930 931 switch (index) { 932 case 0: 933 // "uri" 934 value = m_cms.getRequestContext().getUri(); 935 break; 936 case 1: 937 // "filename" 938 value = m_resourceName; 939 break; 940 case 2: 941 // folder 942 value = m_cms.getRequestContext().getFolderUri(); 943 break; 944 case 3: 945 // default.encoding 946 value = OpenCms.getSystemInfo().getDefaultEncoding(); 947 break; 948 case 4: 949 // remoteaddress 950 value = m_cms.getRequestContext().getRemoteAddress(); 951 break; 952 case 5: 953 // webapp 954 value = OpenCms.getSystemInfo().getWebApplicationName(); 955 break; 956 case 6: 957 // webbasepath 958 value = OpenCms.getSystemInfo().getWebApplicationRfsPath(); 959 break; 960 case 7: 961 // version 962 value = OpenCms.getSystemInfo().getVersionNumber(); 963 break; 964 case 8: 965 // versionid 966 value = OpenCms.getSystemInfo().getVersionId(); 967 break; 968 default: 969 // return the key "as is" 970 value = originalKey; 971 break; 972 } 973 974 return value; 975 } 976 977 if (macro.startsWith(KEY_PARAM)) { 978 String remaining = macro.substring(KEY_PARAM.length()); 979 int colPos = remaining.indexOf(":"); 980 String defaultValue = null; 981 String key = null; 982 if (colPos > -1) { 983 defaultValue = remaining.substring(colPos + 1); 984 key = remaining.substring(0, colPos); 985 } else { 986 key = remaining; 987 } 988 String val = CmsParameterStore.getInstance().getValue(m_cms, key); 989 if (val == null) { 990 val = defaultValue; 991 } 992 if (val == null) { 993 LOG.warn("Parameter not defined: " + remaining); 994 } 995 return val; 996 997 } 998 999 if (macro.startsWith(KEY_SITEMAP_ATTRIBUTE)) { 1000 String remaining = macro.substring(KEY_SITEMAP_ATTRIBUTE.length()); 1001 int colPos = remaining.indexOf(":"); 1002 String defaultValue = null; 1003 String key = null; 1004 if (colPos > -1) { 1005 defaultValue = remaining.substring(colPos + 1); 1006 key = remaining.substring(0, colPos); 1007 } else { 1008 key = remaining; 1009 } 1010 String adeContext = (String)m_cms.getRequestContext().getAttribute( 1011 CmsRequestContext.ATTRIBUTE_ADE_CONTEXT_PATH); 1012 if (adeContext == null) { 1013 adeContext = m_cms.getRequestContext().getRootUri(); 1014 } 1015 CmsADEConfigData config = OpenCms.getADEManager().lookupConfigurationWithCache(m_cms, adeContext); 1016 String val = config.getAttribute(key, defaultValue); 1017 if (val == null) { 1018 LOG.warn("Sitemap attribute not defined: " + key); 1019 } 1020 return val; 1021 1022 } 1023 1024 if (CmsMacroResolver.KEY_CURRENT_USER_NAME.equals(macro)) { 1025 // the key is the current users login name 1026 return m_cms.getRequestContext().getCurrentUser().getName(); 1027 } 1028 1029 if (CmsMacroResolver.KEY_CURRENT_USER_FIRSTNAME.equals(macro)) { 1030 // the key is the current users first name 1031 return m_cms.getRequestContext().getCurrentUser().getFirstname(); 1032 } 1033 1034 if (CmsMacroResolver.KEY_CURRENT_USER_LASTNAME.equals(macro)) { 1035 // the key is the current users last name 1036 return m_cms.getRequestContext().getCurrentUser().getLastname(); 1037 } 1038 1039 if (CmsMacroResolver.KEY_CURRENT_USER_DISPLAYNAME.equals(macro)) { 1040 // the key is the current users display name 1041 try { 1042 if (m_messages != null) { 1043 return m_cms.getRequestContext().getCurrentUser().getDisplayName(m_cms, m_messages.getLocale()); 1044 } else { 1045 return m_cms.getRequestContext().getCurrentUser().getDisplayName( 1046 m_cms, 1047 m_cms.getRequestContext().getLocale()); 1048 } 1049 } catch (CmsException e) { 1050 // ignore, macro can not be resolved 1051 } 1052 } 1053 1054 if (CmsMacroResolver.KEY_CURRENT_ORGUNIT_FQN.equals(macro)) { 1055 // the key is the current organizational unit fully qualified name 1056 return m_cms.getRequestContext().getOuFqn(); 1057 } 1058 1059 if (CmsMacroResolver.KEY_CURRENT_ORGUNIT_DESCRIPTION.equals(macro)) { 1060 // the key is the current organizational unit description 1061 try { 1062 CmsOrganizationalUnit ou = OpenCms.getOrgUnitManager().readOrganizationalUnit( 1063 m_cms, 1064 m_cms.getRequestContext().getOuFqn()); 1065 if (m_messages != null) { 1066 return ou.getDescription(m_messages.getLocale()); 1067 } else { 1068 return ou.getDescription(m_cms.getRequestContext().getLocale()); 1069 } 1070 } catch (CmsException e) { 1071 // ignore, macro can not be resolved 1072 } 1073 } 1074 1075 if (CmsMacroResolver.KEY_CURRENT_USER_FULLNAME.equals(macro)) { 1076 // the key is the current users full name 1077 return m_cms.getRequestContext().getCurrentUser().getFullName(); 1078 } 1079 1080 if (CmsMacroResolver.KEY_CURRENT_USER_EMAIL.equals(macro)) { 1081 // the key is the current users email address 1082 return m_cms.getRequestContext().getCurrentUser().getEmail(); 1083 } 1084 1085 if (CmsMacroResolver.KEY_CURRENT_USER_STREET.equals(macro)) { 1086 // the key is the current users address 1087 return m_cms.getRequestContext().getCurrentUser().getAddress(); 1088 } 1089 1090 if (CmsMacroResolver.KEY_CURRENT_USER_ZIP.equals(macro)) { 1091 // the key is the current users zip code 1092 return m_cms.getRequestContext().getCurrentUser().getZipcode(); 1093 } 1094 1095 if (CmsMacroResolver.KEY_CURRENT_USER_COUNTRY.equals(macro)) { 1096 // the key is the current users country 1097 return m_cms.getRequestContext().getCurrentUser().getCountry(); 1098 } 1099 1100 if (CmsMacroResolver.KEY_CURRENT_USER_CITY.equals(macro)) { 1101 // the key is the current users city 1102 return m_cms.getRequestContext().getCurrentUser().getCity(); 1103 } 1104 1105 if (CmsMacroResolver.KEY_CURRENT_USER_LASTLOGIN.equals(macro) && (m_messages != null)) { 1106 // the key is the current users last login timestamp 1107 return m_messages.getDateTime(m_cms.getRequestContext().getCurrentUser().getLastlogin()); 1108 } 1109 1110 if (CmsMacroResolver.KEY_REQUEST_SITEROOT.equals(macro)) { 1111 // the key is the currently requested site root 1112 return m_cms.getRequestContext().getSiteRoot(); 1113 } 1114 1115 if (CmsMacroResolver.KEY_REQUEST_URI.equals(macro)) { 1116 // the key is the currently requested uri 1117 return m_cms.getRequestContext().getUri(); 1118 } 1119 1120 if (CmsMacroResolver.KEY_REQUEST_FOLDER.equals(macro)) { 1121 // the key is the currently requested folder 1122 return CmsResource.getParentFolder(m_cms.getRequestContext().getUri()); 1123 } 1124 1125 if (CmsMacroResolver.KEY_REQUEST_ENCODING.equals(macro)) { 1126 // the key is the current encoding of the request 1127 return m_cms.getRequestContext().getEncoding(); 1128 } 1129 1130 if (CmsMacroResolver.KEY_REQUEST_LOCALE.equals(macro)) { 1131 // the key is the current locale of the request 1132 return m_cms.getRequestContext().getLocale().toString(); 1133 } 1134 1135 if (CmsMacroResolver.KEY_CONTEXT_PATH.equals(macro)) { 1136 // the key is the OpenCms context path 1137 return OpenCms.getSystemInfo().getContextPath(); 1138 } 1139 1140 if (CmsMacroResolver.KEY_CURRENT_USER_INSTITUTION.equals(macro)) { 1141 // the key is the current users institution 1142 return m_cms.getRequestContext().getCurrentUser().getInstitution(); 1143 } 1144 1145 } 1146 1147 if (CmsMacroResolver.KEY_CURRENT_TIME.equals(macro)) { 1148 // the key is the current system time 1149 return String.valueOf(System.currentTimeMillis()); 1150 } else if (macro.startsWith(CmsMacroResolver.KEY_CURRENT_TIME)) { 1151 // the key starts with the current system time 1152 macro = macro.substring(CmsMacroResolver.KEY_CURRENT_TIME.length()).trim(); 1153 char operator = macro.charAt(0); 1154 macro = macro.substring(1).trim(); 1155 long delta = 0; 1156 try { 1157 delta = Long.parseLong(macro); 1158 } catch (NumberFormatException e) { 1159 // ignore, there will be no delta 1160 } 1161 long resultTime = System.currentTimeMillis(); 1162 switch (operator) { 1163 case '+': 1164 // add delta to current time 1165 resultTime += delta; 1166 break; 1167 case '-': 1168 // subtract delta from current time 1169 resultTime -= delta; 1170 break; 1171 default: 1172 break; 1173 } 1174 return String.valueOf(resultTime); 1175 } 1176 1177 if (CmsMacroResolver.KEY_RANDOM_ID.equals(macro)) { 1178 // a random id value is requested 1179 String id = CmsUUID.getConstantUUID("randomId." + Math.random()).toString(); 1180 // full UUIDs are to long, the first part should be enough 1181 return id.substring(0, id.indexOf('-')); 1182 } 1183 1184 if (m_additionalMacros != null) { 1185 return m_additionalMacros.get(macro); 1186 } 1187 1188 return null; 1189 } 1190 1191 /** 1192 * @see org.opencms.util.I_CmsMacroResolver#isKeepEmptyMacros() 1193 */ 1194 public boolean isKeepEmptyMacros() { 1195 1196 return m_keepEmptyMacros; 1197 } 1198 1199 /** 1200 * Resolves the macros in the given input.<p> 1201 * 1202 * Calls <code>{@link #resolveMacros(String)}</code> until no more macros can 1203 * be resolved in the input. This way "nested" macros in the input are resolved as well.<p> 1204 * 1205 * @see org.opencms.util.I_CmsMacroResolver#resolveMacros(java.lang.String) 1206 */ 1207 public String resolveMacros(String input) { 1208 1209 String result = input; 1210 1211 if (input != null) { 1212 String lastResult; 1213 int count = 0; 1214 do { 1215 // save result for next comparison 1216 lastResult = result; 1217 // resolve the macros 1218 result = CmsMacroResolver.resolveMacros(result, this); 1219 // if nothing changes then the final result is found 1220 count++; 1221 if ((count >= 1000) && LOG.isErrorEnabled()) { 1222 LOG.error( 1223 "Terminated macro resolution after 1000 iterations. Last substitution is \"" 1224 + lastResult 1225 + "\" to \"" 1226 + result 1227 + "\"."); 1228 } 1229 } while (!result.equals(lastResult) && (count < 1000)); 1230 } 1231 1232 // return the result 1233 return result; 1234 } 1235 1236 /** 1237 * Provides a set of additional macros to this macro resolver.<p> 1238 * 1239 * Macros added with {@link #addMacro(String, String)} are added to the same set 1240 * 1241 * @param additionalMacros the additional macros to add 1242 * 1243 * @return this instance of the macro resolver 1244 */ 1245 public CmsMacroResolver setAdditionalMacros(Map<String, String> additionalMacros) { 1246 1247 m_additionalMacros = additionalMacros; 1248 return this; 1249 } 1250 1251 /** 1252 * Provides an OpenCms user context to this macro resolver, required to resolve certain macros.<p> 1253 * 1254 * @param cms the OpenCms user context 1255 * 1256 * @return this instance of the macro resolver 1257 */ 1258 public CmsMacroResolver setCmsObject(CmsObject cms) { 1259 1260 m_cms = cms; 1261 return this; 1262 } 1263 1264 /** 1265 * Provides a JSP page context to this macro resolver, required to resolve certain macros.<p> 1266 * 1267 * @param jspPageContext the JSP page context to use 1268 * 1269 * @return this instance of the macro resolver 1270 */ 1271 public CmsMacroResolver setJspPageContext(PageContext jspPageContext) { 1272 1273 m_jspPageContext = jspPageContext; 1274 return this; 1275 } 1276 1277 /** 1278 * Controls of macros that can't be resolved are left unchanged in the input, 1279 * or are replaced with an empty String.<p> 1280 * 1281 * @param keepEmptyMacros the replacement flag to use 1282 * 1283 * @return this instance of the macro resolver 1284 * 1285 * @see #isKeepEmptyMacros() 1286 */ 1287 public CmsMacroResolver setKeepEmptyMacros(boolean keepEmptyMacros) { 1288 1289 m_keepEmptyMacros = keepEmptyMacros; 1290 return this; 1291 } 1292 1293 /** 1294 * Provides a set of <code>{@link CmsMessages}</code> to this macro resolver, 1295 * required to resolve localized macros.<p> 1296 * 1297 * @param messages the message resource bundle to use 1298 * 1299 * @return this instance of the macro resolver 1300 */ 1301 public CmsMacroResolver setMessages(CmsMessages messages) { 1302 1303 m_messages = messages; 1304 return this; 1305 } 1306 1307 /** 1308 * Sets the parameter map.<p> 1309 * 1310 * @param parameterMap the parameter map to set 1311 */ 1312 public void setParameterMap(Map<String, String[]> parameterMap) { 1313 1314 m_parameterMap = parameterMap; 1315 } 1316 1317 /** 1318 * Provides a resource name to this macro resolver, required to resolve certain macros.<p> 1319 * 1320 * @param resourceName the resource name to use 1321 * 1322 * @return this instance of the macro resolver 1323 */ 1324 public CmsMacroResolver setResourceName(String resourceName) { 1325 1326 m_resourceName = resourceName; 1327 return this; 1328 } 1329 1330 /** 1331 * Returns a function which applies the macro substitution of this resolver to its argument.<p> 1332 * 1333 * @return a function performing string substitution with this resolver 1334 */ 1335 public Function<String, String> toFunction() { 1336 1337 return new Function<String, String>() { 1338 1339 public String apply(String input) { 1340 1341 return resolveMacros(input); 1342 1343 } 1344 }; 1345 } 1346}