PageRenderTime 58ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/src/aribaweb/ariba/ui/aribaweb/core/AWComponent.java

http://aribaweb.googlecode.com/
Java | 1886 lines | 1330 code | 235 blank | 321 comment | 193 complexity | 17d93633f0378329d0b4208c0ac2e2a6 MD5 | raw file
Possible License(s): BSD-3-Clause, Apache-2.0, LGPL-2.1
  1. /*
  2. Copyright 1996-2011 Ariba, Inc.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. $Id: //ariba/platform/ui/aribaweb/ariba/ui/aribaweb/core/AWComponent.java#125 $
  13. */
  14. package ariba.ui.aribaweb.core;
  15. import ariba.ui.aribaweb.util.AWBaseObject;
  16. import ariba.util.core.MapUtil;
  17. import ariba.ui.aribaweb.util.AWCharacterEncoding;
  18. import ariba.ui.aribaweb.util.AWEnvironmentStack;
  19. import ariba.ui.aribaweb.util.AWFastStringBuffer;
  20. import ariba.ui.aribaweb.util.AWGenericException;
  21. import ariba.ui.aribaweb.util.AWEncodedString;
  22. import ariba.ui.aribaweb.util.AWResource;
  23. import ariba.ui.aribaweb.util.AWResourceManager;
  24. import ariba.ui.aribaweb.util.AWStringDictionary;
  25. import ariba.ui.aribaweb.util.AWStringsThunk;
  26. import ariba.ui.aribaweb.util.AWUtil;
  27. import ariba.ui.aribaweb.util.AWParameters;
  28. import ariba.ui.aribaweb.util.AWNamespaceManager;
  29. import ariba.ui.aribaweb.html.AWPrivateScript;
  30. import ariba.util.core.Assert;
  31. import ariba.util.core.ClassUtil;
  32. import ariba.util.core.PerformanceState;
  33. import ariba.util.core.StringUtil;
  34. import ariba.util.core.Constants;
  35. import ariba.util.core.Fmt;
  36. import java.util.Map;
  37. import ariba.util.i18n.I18NUtil;
  38. import java.io.IOException;
  39. import java.io.InputStream;
  40. import java.io.UnsupportedEncodingException;
  41. import java.lang.reflect.Field;
  42. import java.lang.reflect.Method;
  43. import java.util.Locale;
  44. import java.util.TimeZone;
  45. import java.util.Iterator;
  46. import java.util.List;
  47. import javax.servlet.http.HttpSession;
  48. /**
  49. AWComponent is the key class for template/based interactive content. All "pages",
  50. page sub-sections, "wrappers", as well as most controls / widgets, and even control
  51. contructs are implemented as subclasses of AWComponent.
  52. <p/>
  53. Components typically have a {@link AWTemplate template} (.awl file) containing "bare
  54. html text" mixed with {@link AWComponentReference references} to other embedded
  55. AWComponents (or low level {@link ariba.ui.aribaweb.core.AWBindableElement elements}).
  56. These, in turn, have {@link AWBinding bindings} that refer back to the java instance
  57. paired with the template (often using {@link ariba.util.fieldvalue.FieldPath fieldpaths}
  58. to dynamically push/pull values and invoke actions.
  59. <p/>
  60. Components may be either {@link #isStateless() stateless} (pooled) or stateful (bound to their
  61. page instance and stored in the session. (Typically pages, page sub-sections, and
  62. particular rich components are stateful, while simple controls are stateless and
  63. simply push/pull their needed state from their parent component via bindings).
  64. <p/>
  65. Simple subclasses of AWComponent typically declare instance variables (possible public
  66. for use in bindings) as well as action methods (which return {@link AWResponseGenerating reponses}
  67. which are usually just other page-level AWComponent instances for the next page (or null
  68. to rerender the current page while reflecting any updated state).
  69. <p/>
  70. Components participate in the {@link AWCycleable} request handling lifecycle. In addition to
  71. {@link #renderResponse(AWRequestContext, AWComponent)}, {@link #applyValues(AWRequestContext, AWComponent)},
  72. and {@link #invokeAction(AWRequestContext, AWComponent)}, an AWComponent also experiences
  73. {@link #init()}, {@link #awake()}, {@link #sleep()}, and even possibly {@link #hibernate()}.
  74. @aribaapi private
  75. */
  76. public class AWComponent extends AWBaseObject implements AWCycleable, AWCycleableReference, AWResponseGenerating,
  77. AWResponseGenerating.ResponseSubstitution
  78. {
  79. public static final String ComponentTemplateFileExtension = ".awl";
  80. public static final Class ClassObject = AWComponent.class;
  81. private static AWTemplateParser DefaultTemplateParser = null;
  82. protected static final String BindingSuffix = "Binding";
  83. private static AWResourceManager _templateResourceManager = null;
  84. protected AWComponentReference _componentReference;
  85. private AWPage _page;
  86. private AWComponent _parent;
  87. private AWElement _currentTemplateElement;
  88. private AWTemplate _uniqueTemplate;
  89. private Map _extendedFields;
  90. private AWBinding _otherBindingsBinding;
  91. private boolean _isAwake = false;
  92. // to control wacky feature of allowing templates to specify their own encoding:
  93. private static boolean AllowTemplateEncoding = true;
  94. // ** Thread Safety Considerations: Stateful components are never shared
  95. // between threads. Stateless components are often used by different
  96. // threads, however, they are only used by one thread at a time as managed
  97. // by the check-out/in scheme in AWComponentDefinition, so we can consider
  98. // all components to be single threaded.
  99. public AWElement determineInstance (String elementName,
  100. String translatedClassName,
  101. Map bindingsHashtable,
  102. String templateName,
  103. int lineNumber)
  104. {
  105. AWApplication application = (AWApplication)AWConcreteApplication.SharedInstance;
  106. // Must search for elementName first since componentName will always succeed (eg AWComponent)
  107. // so we don't ever try the elementName (which is used for classless components).
  108. AWComponentDefinition componentDefinition = application.componentDefinitionForName(translatedClassName);
  109. if (componentDefinition == null) {
  110. String componentName = ClassUtil.stripPackageFromClassName(getClass().getName());
  111. componentDefinition = application.componentDefinitionForName(componentName);
  112. }
  113. AWComponentReference componentReference = createComponentReference(componentDefinition);
  114. return componentReference.determineInstance(translatedClassName, bindingsHashtable, templateName, lineNumber);
  115. }
  116. protected AWComponentReference createComponentReference (AWComponentDefinition componentDefinition)
  117. {
  118. return AWComponentReference.create(componentDefinition);
  119. }
  120. public AWElement determineInstance (String elementName, Map bindingsHashtable, String templateName, int lineNumber)
  121. {
  122. Assert.assertNonFatal(true, "Do not call determineInstance(String, Map, String, int)");
  123. return determineInstance(elementName, elementName, bindingsHashtable, null, -1);
  124. }
  125. protected boolean useLocalPool ()
  126. {
  127. return false;
  128. }
  129. /**
  130. * Pseudo-private: used only for initing validation testing version of components
  131. * (i.e. for AWConcreteApplication.preinstantiateAllComponents()
  132. */
  133. protected void _setup (AWComponentReference componentReference, AWPage page)
  134. {
  135. _componentReference = componentReference;
  136. setPage(page);
  137. }
  138. public void init (AWComponentReference componentReference, AWComponent parentComponent, AWPage page)
  139. {
  140. _componentReference = componentReference;
  141. _parent = parentComponent;
  142. setPage(page);
  143. this.init();
  144. }
  145. public void init ()
  146. {
  147. super.init();
  148. // If we are truly stateless (isStateless == true and we aren't a page level
  149. // component) make sure the component doesn't override init. It doesn't make
  150. // sense for stateless components to override init because init will only be
  151. // called once (technically, once per shared instance).
  152. if (isStatelessSubComponent()) {
  153. try {
  154. Class cls = getClass();
  155. while (cls != null && cls != AWComponent.ClassObject) {
  156. Method initMethod = cls.getDeclaredMethod("init");
  157. Assert.that(initMethod == null, "Stateless component %s cannot override init().", getClass().getName());
  158. cls = cls.getSuperclass();
  159. }
  160. }
  161. catch (NoSuchMethodException e) {
  162. // Will never happen
  163. e.printStackTrace();
  164. }
  165. }
  166. }
  167. protected void setParent (AWComponent parent)
  168. {
  169. _parent = parent;
  170. }
  171. public AWComponent parent ()
  172. {
  173. return _parent;
  174. }
  175. protected void setPage (AWPage page)
  176. {
  177. _page = page;
  178. }
  179. private boolean isStatelessSubComponent ()
  180. {
  181. return isStateless() && parent() != null;
  182. }
  183. protected void setCurrentTemplateElement (AWElement element)
  184. {
  185. _currentTemplateElement = element;
  186. }
  187. protected AWElement currentTemplateElement ()
  188. {
  189. return _currentTemplateElement;
  190. }
  191. /**
  192. This method allows for access to the current component fromthe .awl via "$this"
  193. */
  194. public AWComponent getThis ()
  195. {
  196. return this;
  197. }
  198. public void setupForNextCycle (AWComponentReference componentReference, AWComponent parentComponent, AWPage page)
  199. {
  200. // This allows a component to be stateless -- these three things change each time a component is reused.
  201. _parent = parentComponent;
  202. _componentReference = componentReference;
  203. setPage(page);
  204. }
  205. public AWComponentDefinition componentDefinition ()
  206. {
  207. return _componentReference.componentDefinition();
  208. }
  209. public boolean hasMultipleTemplates ()
  210. {
  211. return false;
  212. }
  213. public static void setDefaultTemplateParser (AWTemplateParser templateParser)
  214. {
  215. DefaultTemplateParser = templateParser;
  216. }
  217. public static AWTemplateParser defaultTemplateParser ()
  218. {
  219. if (DefaultTemplateParser == null) {
  220. DefaultTemplateParser = new AWHtmlTemplateParser();
  221. DefaultTemplateParser.init((AWApplication)AWConcreteApplication.sharedInstance());
  222. // the AWApi doc tags...
  223. DefaultTemplateParser.registerContainerClassForTagName("Binding", AWBindingApi.class);
  224. DefaultTemplateParser.registerContainerClassForTagName("NamedContent", AWContentApi.class);
  225. DefaultTemplateParser.registerContainerClassForTagName("Copyright", AWHtmlTemplateParser.LiteralContainer.class);
  226. DefaultTemplateParser.registerContainerClassForTagName("Responsible", AWHtmlTemplateParser.LiteralContainer.class);
  227. DefaultTemplateParser.registerContainerClassForTagName("Todo", AWHtmlTemplateParser.LiteralContainer.class);
  228. DefaultTemplateParser.registerContainerClassForTagName("Overview", AWHtmlTemplateParser.LiteralContainer.class);
  229. DefaultTemplateParser.registerContainerClassForTagName("Example", AWExampleApi.class);
  230. DefaultTemplateParser.registerContainerClassForTagName("IncludeExample", AWIncludeExample.class);
  231. DefaultTemplateParser.registerContainerClassForTagName("script", AWPrivateScript.class);
  232. DefaultTemplateParser.registerContainerClassForTagName("Script", AWPrivateScript.class);
  233. DefaultTemplateParser.registerContainerClassForTagName("SCRIPT", AWPrivateScript.class);
  234. }
  235. return DefaultTemplateParser;
  236. }
  237. protected static void initializeAllowedGlobalTags (AWNamespaceManager.AllowedGlobalsResolver resolver)
  238. {
  239. resolver.addAllowedGlobal("Binding");
  240. resolver.addAllowedGlobal("NamedContent");
  241. resolver.addAllowedGlobal("Copyright");
  242. resolver.addAllowedGlobal("Responsible");
  243. resolver.addAllowedGlobal("Todo");
  244. resolver.addAllowedGlobal("Overview");
  245. resolver.addAllowedGlobal("Example");
  246. resolver.addAllowedGlobal("IncludeExample");
  247. resolver.addAllowedGlobal("Script");
  248. }
  249. public AWTemplateParser templateParser ()
  250. {
  251. return _page.templateParser();
  252. }
  253. public void setTemplateParser (AWTemplateParser templateParser)
  254. {
  255. _page.setTemplateParser(templateParser);
  256. }
  257. public AWComponentReference componentReference ()
  258. {
  259. return _componentReference;
  260. }
  261. public static AWComponent createPageWithName (String pageName)
  262. {
  263. AWConcreteApplication application = (AWConcreteApplication)AWConcreteApplication.sharedInstance();
  264. AWRequestContext requestContext = application.createRequestContext(null);
  265. return application.createPageWithName(pageName, requestContext);
  266. }
  267. protected void takeBindings (AWBindingDictionary bindings)
  268. {
  269. if (isStateless()) {
  270. takeBindingsViaReflection(bindings);
  271. }
  272. _otherBindingsBinding = bindings.get(AWBindingNames.otherBindings);
  273. }
  274. private void takeBindingsViaReflection (AWBindingDictionary bindings)
  275. {
  276. Map fields = componentDefinition().bindingFields();
  277. try {
  278. int i = bindings.size();
  279. while (i-- > 0) {
  280. String key = bindings.keyAt(i);
  281. Field field = (Field)fields.get(key);
  282. if (field != null) {
  283. AWBinding binding = bindings.elementAt(i);
  284. if (binding != null) {
  285. // Run this check only in debug mode
  286. if (((AWConcreteApplication)AWConcreteApplication.SharedInstance).isStateValidationEnabled()) {
  287. Object existingValue = field.get(this);
  288. Assert.that(existingValue == null || existingValue == AWBinding.DummyBinding,
  289. "AWBinding fields must not be initialized: %s %s contains %s",
  290. getClass(), field, existingValue);
  291. }
  292. field.set(this, binding);
  293. }
  294. }
  295. }
  296. }
  297. catch (IllegalAccessException illegalAccessException) {
  298. throw new AWGenericException(illegalAccessException);
  299. }
  300. }
  301. private void clearBindingIvars ()
  302. {
  303. Map fields = componentDefinition().bindingFields();
  304. try {
  305. Iterator iter = fields.values().iterator();
  306. while (iter.hasNext()) {
  307. Field field = (Field)iter.next();
  308. field.set(this, null);
  309. }
  310. }
  311. catch (IllegalAccessException illegalAccessException) {
  312. throw new AWGenericException(illegalAccessException);
  313. }
  314. }
  315. // Called when checking a stateless component back into its pool
  316. protected boolean isFieldRequiredClear (Field field) {
  317. // we don't require AWBindings to be cleared for locally pooled components (because they're pooled on the
  318. // AWComponentReference where the bindings stay constant across uses
  319. return super.isFieldRequiredClear(field) &&
  320. (!useLocalPool() || !AWBinding.class.isAssignableFrom(field.getType()))
  321. && !(field.getName().equals("_uniqueTemplate") || field.getName().equals("_extendedFields"));
  322. }
  323. public void ensureFieldValuesClear ()
  324. {
  325. // Stop the check at AWComponent -- i.e. just check ivars introduced in the
  326. // subclasses
  327. ensureFieldValuesClear(getClass(), AWComponent.class);
  328. }
  329. protected Field lookupBindingField (String fieldName, Class classObject)
  330. {
  331. Field field = null;
  332. try {
  333. field = classObject.getDeclaredField(fieldName);
  334. if (field == null) {
  335. field = recurseForLookupBindingField(fieldName, classObject);
  336. }
  337. }
  338. catch (NoSuchFieldException noSuchFieldException) {
  339. field = recurseForLookupBindingField(fieldName, classObject);
  340. }
  341. catch (SecurityException securityException) {
  342. securityException = null;
  343. }
  344. return field;
  345. }
  346. private Field recurseForLookupBindingField (String fieldName, Class classObject)
  347. {
  348. Class superclassObject = classObject.getSuperclass();
  349. return (superclassObject == null || superclassObject == AWComponent.ClassObject.getSuperclass()) ?
  350. null :
  351. lookupBindingField(fieldName, superclassObject);
  352. }
  353. protected Field lookupBindingField (String key)
  354. {
  355. String fieldName = StringUtil.strcat("_", key, BindingSuffix);
  356. return lookupBindingField(fieldName, getClass());
  357. }
  358. /////////////////////////////
  359. // Request Context Accessors
  360. /////////////////////////////
  361. public AWRequestContext requestContext ()
  362. {
  363. return _page.requestContext();
  364. }
  365. public AWApplication application ()
  366. {
  367. return _page.requestContext().application();
  368. }
  369. public AWPage page ()
  370. {
  371. return _page;
  372. }
  373. public AWComponent pageComponent ()
  374. {
  375. return _page.pageComponent();
  376. }
  377. public AWSession session ()
  378. {
  379. return _page.session(true);
  380. }
  381. public AWSession session (boolean required)
  382. {
  383. return _page.session(required);
  384. }
  385. public HttpSession httpSession ()
  386. {
  387. return _page.httpSession();
  388. }
  389. public AWRequest request ()
  390. {
  391. return _page.requestContext().request();
  392. }
  393. public AWResponse response ()
  394. {
  395. return _page.requestContext().response();
  396. }
  397. public boolean isBrowserMicrosoft ()
  398. {
  399. return _page.isBrowserMicrosoft;
  400. }
  401. public boolean isMacintosh ()
  402. {
  403. return _page.isMacintosh;
  404. }
  405. public String browserMinWidth ()
  406. {
  407. return _page.requestContext().browserMinWidth();
  408. }
  409. public String browserMaxWidth ()
  410. {
  411. return _page.requestContext().browserMaxWidth();
  412. }
  413. public AWResourceManager resourceManager ()
  414. {
  415. return _page.resourceManager();
  416. }
  417. public void setResourceManager (AWResourceManager resourceManager)
  418. {
  419. _page.setResourceManager(resourceManager);
  420. }
  421. public void setCharacterEncoding (AWCharacterEncoding characterEncoding)
  422. {
  423. _page.setCharacterEncoding(characterEncoding);
  424. }
  425. public AWCharacterEncoding characterEncoding ()
  426. {
  427. return _page.characterEncoding();
  428. }
  429. public void setClientTimeZone (TimeZone timeZone)
  430. {
  431. _page.setClientTimeZone(timeZone);
  432. }
  433. public TimeZone clientTimeZone ()
  434. {
  435. return _page.clientTimeZone();
  436. }
  437. public void setEnv (AWEnvironmentStack environmentStack)
  438. {
  439. _page.setEnv(environmentStack);
  440. }
  441. public AWEnvironmentStack env ()
  442. {
  443. return _page.env();
  444. }
  445. public void setPreferredLocale (Locale locale)
  446. {
  447. _page.setPreferredLocale(locale);
  448. }
  449. public Locale preferredLocale ()
  450. {
  451. return _page.preferredLocale();
  452. }
  453. public String name ()
  454. {
  455. return componentDefinition().componentName();
  456. }
  457. public String namePath ()
  458. {
  459. return componentDefinition().componentNamePath();
  460. }
  461. public String templateName ()
  462. {
  463. return componentDefinition().templateName();
  464. }
  465. /**
  466. * Get the name of the class where we get our resources from.
  467. * This is the class in which we resolve AWLocal references.
  468. * @return the full class name of the resource class
  469. * @aribaapi ariba
  470. */
  471. public String resourceClassName ()
  472. {
  473. if (!hasMultipleTemplates()) {
  474. // in the normal case, we use the one-time calculated resourceClassName
  475. return componentDefinition().resourceClassName(this);
  476. }
  477. else {
  478. // in the multiple personalities case, get the template name and derive
  479. // a resource class from that.
  480. String templateName = templateName();
  481. return AWComponentDefinition.computeClassNameFromTemplate(templateName);
  482. }
  483. }
  484. public AWTemplate template ()
  485. {
  486. AWTemplate template = _uniqueTemplate;
  487. if (template == null) {
  488. template = loadTemplate();
  489. if (!hasMultipleTemplates() && !AWConcreteApplication.IsRapidTurnaroundEnabled) {
  490. _uniqueTemplate = template;
  491. }
  492. }
  493. return template;
  494. }
  495. public boolean isClientPanel ()
  496. {
  497. return dict("clientPanel") != null;
  498. }
  499. public void setClientPanel (boolean yn)
  500. {
  501. if (yn) {
  502. dict("clientPanel", Boolean.TRUE);
  503. } else {
  504. dict().remove("clientPanel");
  505. }
  506. }
  507. // AWResponseGenerating.ResponseSubstitution
  508. // See if we're a modal panel...
  509. public AWResponseGenerating replacementResponse () {
  510. AWPage requestPage;
  511. if (requestContext() != null && (requestPage = requestContext().requestPage()) != null
  512. && isClientPanel()) {
  513. requestPage.addModalPanel(this);
  514. return requestPage.pageComponent();
  515. }
  516. return this;
  517. }
  518. protected AWApi componentApi ()
  519. {
  520. // make sure the template is loaded
  521. if (hasTemplate()) {
  522. template();
  523. return componentDefinition().componentApi();
  524. }
  525. return null;
  526. }
  527. ////////////////////
  528. // Validation
  529. ////////////////////
  530. protected void addValidationError (AWRequestContext requestContext)
  531. {
  532. // AWValidationContext validationContext = requestContext.validationContext();
  533. // validationContext.addComponentWithError(componentDefinition());
  534. }
  535. protected void validate (AWValidationContext validationContext)
  536. {
  537. AWTemplate template = template();
  538. if (template != null && !template.validated()) {
  539. template.validate(validationContext,this);
  540. }
  541. if (componentDefinition().hasValidationErrors() && isValidationEnabled()) {
  542. validationContext.addComponentWithError(componentDefinition());
  543. }
  544. }
  545. protected String getCurrentPackageName ()
  546. {
  547. return componentDefinition().componentPackageName();
  548. }
  549. // Override this method to bypass the package level check
  550. public boolean isValidationEnabled ()
  551. {
  552. return isPackageLevelFlagEnabled(getCurrentPackageName(), AWConcreteApplication.TemplateValidationFlag);
  553. }
  554. // Override this method to bypass the package level check
  555. public boolean isStrictTagNaming ()
  556. {
  557. return isPackageLevelFlagEnabled(getCurrentPackageName(), AWConcreteApplication.StrictTagNamingFlag);
  558. }
  559. protected boolean isPackageLevelFlagEnabled (String packageName, int flag)
  560. {
  561. return ((AWConcreteApplication)AWConcreteApplication.sharedInstance()).isPackageLevelFlagEnabled(packageName, flag);
  562. }
  563. ////////////////////
  564. // Template parsing
  565. ////////////////////
  566. public boolean allowsWhitespaceCompression ()
  567. {
  568. return !AWConcreteApplication.IsDebuggingEnabled;
  569. }
  570. private void throwComponentNotFoundException (String templateName)
  571. {
  572. throw new AWGenericException("*** Error: " + getClass().getName() + ": unable to locate file named \"" + templateName + "\"");
  573. }
  574. protected AWResource safeTemplateResource ()
  575. {
  576. AWResourceManager resourceManager = templateResourceManager();
  577. String templateName = templateName();
  578. AWResource resource = resourceManager.resourceNamed(templateName);
  579. if (resource == null) {
  580. resource = parentTemplateResource();
  581. }
  582. return resource;
  583. }
  584. protected AWResource parentTemplateResource ()
  585. {
  586. // Recurse up the superclass chain to locate a template. We used to require subclasses
  587. // to explicitly provide the templateName of the superclass, but now we iteratively
  588. // locate the template resource as follows:
  589. AWResource resource = null;
  590. AWResourceManager resourceManager = templateResourceManager();
  591. Class superclass = getClass().getSuperclass();
  592. while (resource == null && AWComponent.ClassObject.isAssignableFrom(superclass)) {
  593. String superclassName = superclass.getName();
  594. AWComponentDefinition componentDefinition = application().componentDefinitionForName(superclassName);
  595. String templateName = componentDefinition.templateName();
  596. resource = resourceManager.resourceNamed(templateName);
  597. superclass = superclass.getSuperclass();
  598. }
  599. return resource;
  600. }
  601. public AWResource templateResource ()
  602. {
  603. AWResource resource = safeTemplateResource();
  604. if (resource == null) {
  605. throwComponentNotFoundException(templateName());
  606. }
  607. return resource;
  608. }
  609. public static AWResourceManager templateResourceManager ()
  610. {
  611. return _templateResourceManager;
  612. }
  613. /**
  614. * We need to use the same resource manager for loading templates.
  615. * Otherwise, each component instances can potential get templates with
  616. * a different structure (ie, AWIncludeComponent maps)
  617. */
  618. public static void initTemplateResourceManager (AWResourceManager manager)
  619. {
  620. _templateResourceManager = manager;
  621. }
  622. protected boolean hasTemplate ()
  623. {
  624. return safeTemplateResource() != null;
  625. }
  626. protected boolean templateHasEncoding ()
  627. {
  628. return false;
  629. }
  630. public boolean hasContentNamed (String name)
  631. {
  632. AWElement contentElement = componentReference().contentElement();
  633. if (name == null) return (contentElement != null);
  634. if (contentElement instanceof AWTemplate) {
  635. AWTemplate template = (AWTemplate)contentElement;
  636. int index = template.indexOfNamedSubtemplate(name, this);
  637. if (index != -1) {
  638. AWContent content = template.subtemplateAt(index);
  639. return content.enabled(this);
  640. }
  641. }
  642. return false;
  643. }
  644. /**
  645. * @deprecated Use hasContentNamed()
  646. */
  647. public boolean hasSubTemplateNamed (String templateName)
  648. {
  649. return hasContentNamed(templateName);
  650. }
  651. protected boolean doesReferenceHaveNamedTemplate (String templateName)
  652. {
  653. AWComponentReference componentReference = componentReference();
  654. return componentReference == null ? false : componentReference.hasNamedTemplate(parent(), templateName);
  655. }
  656. protected static String readTemplateString (InputStream inputStream)
  657. {
  658. byte[] bytes = AWUtil.getBytes(inputStream);
  659. String templateString = AWCharacterEncoding.ISO8859_1.newString(bytes);
  660. if (AllowTemplateEncoding && templateString.startsWith("<!--")) {
  661. int indexOfClosingComment = templateString.indexOf("-->");
  662. if (indexOfClosingComment != -1) {
  663. String firstLine = templateString.substring(0, indexOfClosingComment);
  664. String encodingToken = "encoding:";
  665. int indexOfEncoding = AWUtil.indexOf(firstLine, encodingToken, 0, true);
  666. if (indexOfEncoding != -1) {
  667. String encodingName = firstLine.substring(indexOfEncoding + encodingToken.length(), indexOfClosingComment);
  668. encodingName = encodingName.trim();
  669. try {
  670. templateString = new String(bytes, encodingName);
  671. }
  672. catch (UnsupportedEncodingException unsupportedEncodingException) {
  673. throw new AWGenericException(unsupportedEncodingException);
  674. }
  675. }
  676. }
  677. }
  678. return templateString;
  679. }
  680. protected InputStream templateInputStream ()
  681. {
  682. AWResource templateResource = templateResource();
  683. return templateResource.inputStream();
  684. }
  685. protected AWTemplate parseTemplate (InputStream fileInputStream)
  686. {
  687. AWTemplate template = null;
  688. AWTemplateParser templateParser = templateParser();
  689. try {
  690. String templateString = null;
  691. boolean shouldExpectEncoding = templateHasEncoding();
  692. if (shouldExpectEncoding) {
  693. templateString = AWUtil.stringWithContentsOfInputStream(fileInputStream, shouldExpectEncoding);
  694. }
  695. else {
  696. templateString = readTemplateString(fileInputStream);
  697. }
  698. if (allowsWhitespaceCompression()) {
  699. templateString = AWUtil.leftJustify(templateString);
  700. }
  701. if (templateParser instanceof AWHtmlTemplateParser) {
  702. template = ((AWHtmlTemplateParser)templateParser).templateFromString(templateString, templateName(), this);
  703. }
  704. else {
  705. template = templateParser.templateFromString(templateString, templateName());
  706. }
  707. }
  708. finally {
  709. try {
  710. fileInputStream.close();
  711. }
  712. catch (IOException ioexception) {
  713. String message = Fmt.S("Error closing file: \"%s\"", templateName());
  714. throw new AWGenericException(message, ioexception);
  715. }
  716. }
  717. return template;
  718. }
  719. protected AWTemplate parseTemplate ()
  720. {
  721. InputStream fileInputStream = templateInputStream();
  722. return parseTemplate(fileInputStream);
  723. }
  724. public AWTemplate loadTemplate ()
  725. {
  726. AWTemplate template = null;
  727. AWResource resource = templateResource();
  728. if (resource != null) {
  729. template = (AWTemplate)resource.object();
  730. if ((template == null) ||
  731. (AWConcreteApplication.IsRapidTurnaroundEnabled
  732. && requestContext().currentPhase() == AWRequestContext.Phase_Render
  733. && resource.hasChanged())) {
  734. // reset needs to be called before template parser since the template parser
  735. // can append validation errors
  736. componentDefinition().resetValidationData();
  737. template = parseTemplate();
  738. if (template != null) {
  739. AWApi componentApi = ((AWConcreteTemplate)template).removeApiTag();
  740. if (AWConcreteApplication.IsDebuggingEnabled) {
  741. componentDefinition().setComponentApi(componentApi);
  742. // validate the componentAPI. Needed since componentAPI
  743. // validation initializes supported binding information.
  744. if (componentApi != null && requestContext() != null) {
  745. // if there is a component api, then validate it (metadata validation)
  746. componentApi.validate(requestContext().validationContext(), this);
  747. }
  748. }
  749. resource.setObject(template);
  750. }
  751. }
  752. }
  753. return template;
  754. }
  755. ////////////////////
  756. // Parser callbacks
  757. ////////////////////
  758. public boolean allowEmbeddedKeyPaths ()
  759. {
  760. return true;
  761. }
  762. /**
  763. Wacky: this method is called when this (stateful) component instance that is about
  764. to be activate is using an out of date class (do to dynamic reloading). If a non-null
  765. instance is returned, it is used in place of this instance. If null is returned,
  766. the stale instance is used.
  767. This is only called in dubug mode (i.e. IsRapidTurnaroundEnabled == true)
  768. */
  769. protected AWComponent replacementInstance (AWComponent parentComponent)
  770. {
  771. AWComponent replacement = null;
  772. // by default, stateful *sub*-components are replaced, but pageComponents are not
  773. if (parentComponent != null) {
  774. replacement = componentDefinition().createComponent(componentReference(), parentComponent, requestContext());
  775. replacement.ensureAwake(page());
  776. }
  777. return replacement;
  778. }
  779. /**
  780. Overridden by AWComponent subclasses to indicate whether component instances
  781. should be preseved with the page/session (i.e. are "stateful") or can be
  782. pooled and reused for each phase of request processing (i.e. are stateless)
  783. Default is to be statelss unless the component is used as the top-level
  784. (page) component.
  785. */
  786. public boolean isStateless ()
  787. {
  788. // default is true (and by default components aren't client panels...)
  789. return !isClientPanel();
  790. }
  791. protected void saveInPage (AWElementIdPath elementIdPath)
  792. {
  793. requestContext().putStatefulComponent(elementIdPath, this);
  794. }
  795. ///////////////
  796. // AWCycleable
  797. ///////////////
  798. public void applyValues(AWRequestContext requestContext, AWComponent component)
  799. {
  800. template().applyValues(requestContext, this);
  801. }
  802. public AWResponseGenerating invokeAction(AWRequestContext requestContext, AWComponent component)
  803. {
  804. return template().invokeAction(requestContext, this);
  805. }
  806. public void renderResponse(AWRequestContext requestContext, AWComponent component)
  807. {
  808. if (AWConcreteApplication.IsDebuggingEnabled) {
  809. validate(requestContext.validationContext());
  810. }
  811. if (!_componentReference.isStateless()) {
  812. AWBacktrackState backtrackState = requestContext.backtrackState();
  813. if ((backtrackState != null) && (backtrackState.component == this)) {
  814. Object userBacktrackState = backtrackState.userState;
  815. Object existingUserBacktrackState = restoreFromBacktrackState(userBacktrackState);
  816. _page.swapBacktrackStates(existingUserBacktrackState);
  817. }
  818. }
  819. template().renderResponse(requestContext, this);
  820. }
  821. // these should be called when not calling via ComponentReference
  822. public void _topLevelApplyValues (AWRequestContext requestContext, AWComponent component)
  823. {
  824. applyValues(requestContext, component);
  825. }
  826. public AWResponseGenerating _topLevelInvokeAction(AWRequestContext requestContext, AWComponent component)
  827. {
  828. return invokeAction(requestContext, this);
  829. }
  830. public void _topLevelRenderResponse(AWRequestContext requestContext, AWComponent component)
  831. {
  832. renderResponse(requestContext, this);
  833. }
  834. public AWEncodedString escapeAttribute (AWEncodedString attributeValue)
  835. {
  836. return useXmlEscaping() ? attributeValue.xmlEscapedString() : attributeValue.htmlAttributeString();
  837. }
  838. public AWEncodedString escapeString (Object object)
  839. {
  840. return useXmlEscaping() ? AWUtil.escapeXml(object) : AWUtil.escapeHtml(object);
  841. }
  842. public AWEncodedString escapeUnsafeString (Object object)
  843. {
  844. return useXmlEscaping() ? AWUtil.escapeXml(object) :
  845. AWUtil.escapeUnsafeHtml(object);
  846. }
  847. public boolean shouldCloseElements ()
  848. {
  849. return useXmlEscaping();
  850. }
  851. public boolean useXmlEscaping ()
  852. {
  853. return false;
  854. }
  855. //////////////////////////
  856. // Page Cache Management
  857. //////////////////////////
  858. public void recordBacktrackState (Object userBacktrackState)
  859. {
  860. if (_componentReference.isStateless()) {
  861. throw new AWGenericException("Attempt to recordBacktrackState(Object) from stateless component.");
  862. }
  863. _page.recordBacktrackState(this, userBacktrackState);
  864. }
  865. public void recordBacktrackState (int userBacktrackState)
  866. {
  867. recordBacktrackState(Constants.getInteger(userBacktrackState));
  868. }
  869. public void removeBacktrackState ()
  870. {
  871. _page.removeBacktrackState();
  872. }
  873. public void truncateBacktrackState ()
  874. {
  875. _page.truncateBacktrackState();
  876. }
  877. public void truncateBacktrackState (AWBacktrackState backtrackStateMark)
  878. {
  879. _page.truncateBacktrackState(backtrackStateMark);
  880. }
  881. public AWBacktrackState markBacktrackState ()
  882. {
  883. return _page.markBacktrackState();
  884. }
  885. public Object restoreFromBacktrackState (Object userBacktrackState)
  886. {
  887. throw new AWGenericException(getClass().getName() + ": backtrack state was found, but component did not implement takeValuesFromBacktrackState(Object userState)");
  888. }
  889. protected boolean shouldRedirect ()
  890. {
  891. return true;
  892. }
  893. public boolean shouldCachePage ()
  894. {
  895. return true;
  896. }
  897. ////////////////////////
  898. // AWResponseGenerating
  899. ////////////////////////
  900. public AWResponse generateResponse (AWResponse response, AWRequestContext requestContext)
  901. {
  902. _page.ensureAwake(requestContext);
  903. requestContext.setPage(_page);
  904. return requestContext.generateResponse(response);
  905. }
  906. public AWResponse generateResponse (AWResponse response)
  907. {
  908. AWRequestContext existingRequestContext = requestContext();
  909. AWRequest request = existingRequestContext == null ? null : existingRequestContext.request();
  910. AWApplication application = (AWApplication)AWConcreteApplication.sharedInstance();
  911. AWRequestContext requestContext = application.createRequestContext(request);
  912. if (existingRequestContext != null) {
  913. HttpSession existingHttpSession = existingRequestContext.existingHttpSession();
  914. if (existingHttpSession != null) {
  915. requestContext.setHttpSession(existingHttpSession);
  916. }
  917. }
  918. return generateResponse(response, requestContext);
  919. }
  920. public AWResponse generateResponse ()
  921. {
  922. return generateResponse(null);
  923. }
  924. public String generateStringContents ()
  925. {
  926. AWRequestContext newRequestContext = null;
  927. AWApplication application = (AWApplication)AWConcreteApplication.sharedInstance();
  928. AWRequestContext existingRequestContext = requestContext();
  929. if (existingRequestContext != null) {
  930. newRequestContext = application.createRequestContext(existingRequestContext.request());
  931. HttpSession existingHttpSession =
  932. existingRequestContext.existingHttpSession();
  933. newRequestContext.setHttpSession(existingHttpSession);
  934. }
  935. else {
  936. newRequestContext = application.createRequestContext(null);
  937. }
  938. // disable incremental update since we're trying to get the string contents
  939. newRequestContext.isContentGeneration(true);
  940. AWResponse response = generateResponse(null, newRequestContext);
  941. return response.generateStringContents();
  942. }
  943. /////////////
  944. // Awake
  945. /////////////
  946. protected void awake ()
  947. {
  948. // Default is to do nothing. Users can override, but are not required to call super.
  949. }
  950. protected void flushState ()
  951. {
  952. clearDict(this);
  953. }
  954. public void ensureAwake (AWPage page)
  955. {
  956. if (!_isAwake) {
  957. setPage(page);
  958. awake();
  959. _isAwake = true;
  960. }
  961. }
  962. protected void sleep ()
  963. {
  964. // Default is to do nothing. Users can override, but are not required to call super.
  965. }
  966. protected void ensureAsleep ()
  967. {
  968. if (_isAwake) {
  969. _isAwake = false;
  970. sleep();
  971. _currentTemplateElement = null;
  972. if (isStatelessSubComponent() && !useLocalPool()) {
  973. _otherBindingsBinding = null;
  974. }
  975. // note: _parent, _componentReference, and _page are set to null in AWComponentRef (but only for stateless);
  976. }
  977. }
  978. protected void hibernate ()
  979. {
  980. // Default is to do nothing. Users can override, but are not required to call super.
  981. }
  982. /**
  983. * Called when the page this component is associated with is no longer on the top
  984. * of the page cache (ie, the user has "moved off" the page). Note that this is
  985. * only called on the "page component" (ie, the component that is the root of the
  986. * component hierarchy for this page).
  987. * @aribaapi private
  988. */
  989. protected void exit ()
  990. {
  991. //Log.aribaweb_request.debug("Exiting: %s",componentPath().toString());
  992. }
  993. ///////////////////
  994. // Bindings Support
  995. ///////////////////
  996. public String[] supportedBindingNames ()
  997. {
  998. return null;
  999. }
  1000. public AWBindingDictionary bindings ()
  1001. {
  1002. return _componentReference.bindings();
  1003. }
  1004. public AWBindingDictionary otherBindings ()
  1005. {
  1006. return _componentReference.otherBindings();
  1007. }
  1008. public AWStringDictionary otherBindingsValues ()
  1009. {
  1010. // This merges otherBindings from the parent with other
  1011. // bindings bound to this component giving preference to the
  1012. // parent's bindings (allow parent to override child).
  1013. // Is this *always* what we want, or do we need to allow
  1014. // developers to decide how to merge things?
  1015. AWStringDictionary otherBindingsValues = (AWStringDictionary)
  1016. ((_otherBindingsBinding == null) ?
  1017. valueForBinding(AWBindingNames.otherBindings) :
  1018. valueForBinding(_otherBindingsBinding));
  1019. AWBindingDictionary bindingDictionary = _componentReference.otherBindings();
  1020. if (bindingDictionary != null) {
  1021. if (otherBindingsValues == null) {
  1022. otherBindingsValues = _page.otherBindingsValuesScratch();
  1023. }
  1024. for (int index = (bindingDictionary.size() - 1); index >= 0;index--) {
  1025. AWEncodedString currentBindingName = bindingDictionary.nameAt(index);
  1026. AWBinding currentBinding = bindingDictionary.elementAt(index);
  1027. AWEncodedString currentBindingValue = encodedStringValueForBinding(currentBinding);
  1028. if (currentBindingValue != null) {
  1029. otherBindingsValues.putIfIdenticalKeyAbsent(currentBindingName, currentBindingValue);
  1030. }
  1031. }
  1032. }
  1033. return otherBindingsValues;
  1034. }
  1035. public AWBinding bindingForName (String bindingName)
  1036. {
  1037. // This is provided for backward compatibility -- not recommended for use.
  1038. return bindingForName(bindingName, true);
  1039. }
  1040. //public static AWMultiKeyHashtable BindingCounts = new AWMultiKeyHashtable(2);
  1041. //public static int BindingCountTotal = 0;
  1042. public AWBinding bindingForName (String bindingName, boolean recursive)
  1043. {
  1044. /*
  1045. BindingCountTotal++;
  1046. Integer bindingCountObj = (Integer)BindingCounts.get(bindingName, componentDefinition());
  1047. int bindingCount = bindingCountObj == null ? 1 : bindingCountObj.intValue() + 1;
  1048. BindingCounts.put(bindingName, componentDefinition(), Constants.getInteger(bindingCount));
  1049. if (BindingCountTotal % 5000 == 0) {
  1050. System.out.println("***** BindingCountTotal: " + BindingCountTotal);
  1051. BindingCounts.printf();
  1052. }
  1053. */
  1054. AWBinding localBinding = _componentReference.bindingForName(bindingName, _parent);
  1055. if (recursive && !hasBinding(localBinding)) {
  1056. localBinding = null;
  1057. }
  1058. return localBinding;
  1059. }
  1060. public boolean hasBinding (String bindingName)
  1061. {
  1062. return bindingForName(bindingName, true) != null;
  1063. }
  1064. public boolean hasBinding (AWBinding binding)
  1065. {
  1066. return binding == null ? false : binding.bindingExistsInParentForSubcomponent(this);
  1067. }
  1068. // Int
  1069. public double doubleValueForBinding (AWBinding binding)
  1070. {
  1071. return (binding != null) ? binding.doubleValue(_parent) : 0.0;
  1072. }
  1073. public double doubleValueForBinding (String bindingName)
  1074. {
  1075. AWBinding binding = bindingForName(bindingName, false);
  1076. return doubleValueForBinding(binding);
  1077. }
  1078. public double doubleValueForBinding (String bindingName, double defaultValue)
  1079. {
  1080. AWBinding binding = bindingForName(bindingName, false);
  1081. return hasBinding(binding) ? doubleValueForBinding(binding) : defaultValue;
  1082. }
  1083. // Int
  1084. public int intValueForBinding (AWBinding binding)
  1085. {
  1086. int intValue = 0;
  1087. if (binding != null) {
  1088. intValue = binding.intValue(_parent);
  1089. }
  1090. return intValue;
  1091. }
  1092. public int intValueForBinding (String bindingName)
  1093. {
  1094. AWBinding binding = bindingForName(bindingName, false);
  1095. return intValueForBinding(binding);
  1096. }
  1097. public int intValueForBinding (String bindingName, int defaultValue)
  1098. {
  1099. AWBinding binding = bindingForName(bindingName, false);
  1100. return hasBinding(binding) ? intValueForBinding(binding) : defaultValue;
  1101. }
  1102. // Boolean
  1103. public boolean booleanValueForBinding (AWBinding binding)
  1104. {
  1105. return (binding != null) && binding.booleanValue(_parent);
  1106. }
  1107. public boolean booleanValueForBinding (String bindingName)
  1108. {
  1109. AWBinding binding = bindingForName(bindingName, false);
  1110. return booleanValueForBinding(binding);
  1111. }
  1112. public boolean booleanValueForBinding (AWBinding binding, boolean defaultValue)
  1113. {
  1114. return hasBinding(binding) ? booleanValueForBinding(binding) : defaultValue;
  1115. }
  1116. public boolean booleanValueForBinding (String bindingName, boolean defaultValue)
  1117. {
  1118. AWBinding binding = bindingForName(bindingName, false);
  1119. return booleanValueForBinding(binding, defaultValue);
  1120. }
  1121. // Object
  1122. public Object valueForBinding (AWBinding binding)
  1123. {
  1124. Object objectValue = null;
  1125. if (binding != null) {
  1126. objectValue = binding.value(_parent);
  1127. }
  1128. return objectValue;
  1129. }
  1130. public Object valueForBinding (String bindingName)
  1131. {
  1132. AWBinding binding = bindingForName(bindingName, false);
  1133. return valueForBinding(binding);
  1134. }
  1135. public Object valueForBinding (String bindingName, Object defaultValue)
  1136. {
  1137. AWBinding binding = bindingForName(bindingName, false);
  1138. return hasBinding(binding) ? valueForBinding(binding) : defaultValue;
  1139. }
  1140. // String
  1141. public String stringValueForBinding (AWBinding binding)
  1142. {
  1143. String stringValue = null;
  1144. if (binding != null) {
  1145. stringValue = binding.stringValue(_parent);
  1146. }
  1147. return stringValue;
  1148. }
  1149. public String stringValueForBinding (String bindingName)
  1150. {
  1151. AWBinding binding = bindingForName(bindingName, false);
  1152. return stringValueForBinding(binding);
  1153. }
  1154. public String stringValueForBinding (String bindingName, String defaultValue)
  1155. {
  1156. AWBinding binding = bindingForName(bindingName, false);
  1157. return hasBinding(binding) ? stringValueForBinding(binding) : defaultValue;
  1158. }
  1159. // AWEncodedString
  1160. public AWEncodedString encodedStringValueForBinding (AWBinding binding)
  1161. {
  1162. AWEncodedString encodedString = null;
  1163. if (binding != null) {
  1164. encodedString = binding.encodedStringValue(_parent);
  1165. }
  1166. return encodedString;
  1167. }
  1168. public AWEncodedString encodedStringValueForBinding (String bindingName)
  1169. {
  1170. AWBinding binding = bindingForName(bindingName, false);
  1171. return encodedStringValueForBinding(binding);
  1172. }
  1173. // Object
  1174. public void setValueForBinding (Object objectValue, AWBinding binding)
  1175. {
  1176. if (binding != null) {
  1177. binding.setValue(objectValue, _parent);
  1178. }
  1179. }
  1180. public void setValueForBinding (Object objectValue, String bindingName)
  1181. {
  1182. AWBinding binding = bindingForName(bindingName, false);
  1183. setValueForBinding(objectValue, binding);
  1184. }
  1185. // ** Int
  1186. public void setValueForBinding (int intValue, AWBinding binding)
  1187. {
  1188. if (binding != null) {
  1189. binding.setValue(intValue, _parent);
  1190. }
  1191. }
  1192. public void setValueForBinding (int intValue, String bindingName)
  1193. {
  1194. AWBinding binding = bindingForName(bindingName, false);
  1195. setValueForBinding(intValue, binding);
  1196. }
  1197. // ** Booelan
  1198. public void setValueForBinding (boolean booleanValue, AWBinding binding)
  1199. {
  1200. if (binding != null) {
  1201. binding.setValue(booleanValue, _parent);
  1202. }
  1203. }
  1204. public void setValueForBinding (boolean booleanValue, String bindingName)
  1205. {
  1206. AWBinding binding = bindingForName(bindingName, false);
  1207. setValueForBinding(booleanValue, binding);
  1208. }
  1209. ///////////////////
  1210. // "xml" support
  1211. ///////////////////
  1212. public static void registerXmlNodeWithName (String xmlNodeName)
  1213. {
  1214. defaultTemplateParser().registerContainerClassForTagName(xmlNodeName, AWConcreteXmlNode.class);
  1215. defaultTemplateParser().registerElementClassForTagName(xmlNodeName, AWConcreteXmlNode.class);
  1216. }
  1217. public AWXmlNode xml ()
  1218. {
  1219. // Perf: recycling opportunity?
  1220. return new AWXmlContext(_componentReference.xmlNode(), _parent);
  1221. }
  1222. /////////////////
  1223. // Convenience
  1224. /////////////////
  1225. public AWComponent pageWithName (String pageName)
  1226. {
  1227. return _page.requestContext().pageWithName(pageName);
  1228. }
  1229. public AWComponent pageWithName (String pageName, Map<String, Object>assignments)
  1230. {
  1231. return _page.requestContext().pageWithName(pageName, assignments);
  1232. }
  1233. public <T> T pageWithClass (Class<T> tClass)
  1234. {
  1235. return (T)pageWithName(tClass.getName());
  1236. }
  1237. public <T> T pageWithClass (Class<T> tClass, Map<String, Object>assignments)
  1238. {
  1239. return (T)pageWithName(tClass.getName(), assignments);
  1240. }
  1241. public Object componentConfiguration(String configName)
  1242. {
  1243. return _page.componentConfiguration(this, configName);
  1244. }
  1245. public void setComponentConfiguration(String configName, Object config)
  1246. {
  1247. _page.setComponentConfiguration(this, configName, config);
  1248. }
  1249. ///////////////////
  1250. // Dict Support
  1251. ///////////////////
  1252. public Map extendedFields ()
  1253. {
  1254. if (_extendedFields == null) {
  1255. _extendedFields = MapUtil.map();
  1256. }
  1257. return _extendedFields;
  1258. }
  1259. protected void clearDict (AWComponent component)
  1260. {
  1261. if (_extendedFields != null) {
  1262. _extendedFields.clear();
  1263. }
  1264. }
  1265. public Map dict ()
  1266. {
  1267. // don't call extendedFields() so subclasses can implement Extensible
  1268. // without rerouting dict()
  1269. if (_extendedFields == null) {
  1270. _extendedFields = MapUtil.map();
  1271. }
  1272. return _extendedFields;
  1273. }
  1274. public void dict (String key, Object value)
  1275. {
  1276. dict().put(key, value);
  1277. }
  1278. public Object dict (String key)
  1279. {
  1280. return (_extendedFields == null) ? null : dict().get(key);
  1281. }
  1282. protected Map pageDict ()
  1283. {
  1284. return _page.pageComponent().dict();
  1285. }
  1286. public AWPageRedirect redirectToPage (AWComponent destinationPage)
  1287. {
  1288. // this should be an AWUtil static method ??
  1289. if (destinationPage == null) {
  1290. destinationPage = _page.pageComponent();
  1291. }
  1292. AWPageRedirect pageRedirect = (AWPageRedirect)pageWithName(AWPageRedirect.PageName);
  1293. pageRedirect.setPage(destinationPage);
  1294. return pageRedirect;
  1295. }
  1296. /////////////////
  1297. // Debugging
  1298. /////////////////
  1299. public AWComponent awcyclePageAndLog ()
  1300. {
  1301. debugString("*** awcyclePageAndLog");
  1302. return null;
  1303. }
  1304. /**
  1305. Create a handy identifier for this component.
  1306. This method creates an identifier composed of the containing template
  1307. location and the class name.
  1308. @return the identifier
  1309. @aribaapi private
  1310. */
  1311. protected final String debugIdentifier ()
  1312. {
  1313. String classname = this.getClass().getName();
  1314. String template = parent().fullTemplateResourceUrl();
  1315. AWBaseElement elem = (AWBaseElement)parent().currentTemplateElement();
  1316. int lineNumber = elem.lineNumber();
  1317. return Fmt.S("%s:%s.%s", classname, template,
  1318. Constants.getInteger(lineNumber));
  1319. }
  1320. protected String fullTemplateResourceUrl ()
  1321. {
  1322. return templateResource().fullUrl();
  1323. }
  1324. public AWFastStringBuffer componentPath (String separatorString)
  1325. {
  1326. AWFastStringBuffer stringBuffer = new AWFastStringBuffer();
  1327. AWComponent currentComponent = this;
  1328. while (currentComponent != null) {
  1329. stringBuffer.append(" ");
  1330. if (currentComponent.getClass() == AWComponent.ClassObject) {
  1331. AWResource resource = currentComponent.templateResource();
  1332. if (resource != null) {
  1333. stringBuffer.append(resource.fullUrl());
  1334. }
  1335. else {
  1336. stringBuffer.append(currentComponent.namePath());
  1337. stringBuffer.append(".awl");
  1338. }
  1339. AWBaseElement elem = (AWBaseElement)currentComponent.currentTemplateElement();
  1340. if (elem != null) {
  1341. stringBuffer.append(":");
  1342. stringBuffer.append(elem.lineNumber());
  1343. }
  1344. }
  1345. else {
  1346. AWBaseElement elem = (AWBaseElement)currentComponent.currentTemplateElement();
  1347. if (elem instanceof AWBindableElement) {
  1348. AWBindableElement bindable = (AWBindableElement)elem;
  1349. stringBuffer.append(bindable.tagName());
  1350. }
  1351. else {
  1352. // needed by toolkit / non-bindeable element based code
  1353. stringBuffer.append(currentComponent.toString());
  1354. }
  1355. if (elem != null) {
  1356. stringBuffer.append("(");
  1357. stringBuffer.append(currentComponent.fullTemplateResourceUrl().replaceAll("^file\\:/", "/"));
  1358. stringBuffer.append(":");
  1359. stringBuffer.append(elem.lineNumber());
  1360. stringBuffer.append(")");
  1361. }
  1362. }
  1363. stringBuffer.append(separatorString);
  1364. currentComponent = currentComponent.parent();
  1365. }
  1366. return stringBuffer;
  1367. }
  1368. protected AWFastStringBuffer componentPath ()
  1369. {
  1370. return componentPath(": ");
  1371. }
  1372. protected String operationIdentifierForKeyPath (String keyPath)
  1373. {
  1374. return null;
  1375. }
  1376. ////////////////////////
  1377. // Formatter Validation
  1378. ////////////////////////
  1379. /**
  1380. * Retrieve the error manager for the page.
  1381. * @return error manager
  1382. */
  1383. public AWErrorManager errorManager ()
  1384. {
  1385. return _page.errorManager();
  1386. }
  1387. public AWFormValueManager formValueManager ()
  1388. {
  1389. return _page.formValueManager();
  1390. }
  1391. /**
  1392. * Record an error to the error manager.
  1393. * To avoid confusion between the current and new ErrorManagers,
  1394. * this is the only way to access the setErrorMessageAndValue().
  1395. * @param errorKey The object that identifies the error. This is typically a string.
  1396. * @param errorMessage The message that describes the error.
  1397. * @param errantValue The unparsable value that the user entered. Since
  1398. the parsing failed, we cannot store this value in
  1399. the field. We stash it away here so we can display
  1400. in the UI later.
  1401. * @aribaapi ariba
  1402. */
  1403. public void recordValidationError (Object errorKey,
  1404. String errorMessage,
  1405. Object errantValue)
  1406. {
  1407. Assert.that(errorKey != null, "errorKey cannot be null");
  1408. AWErrorInfo error = new AWErrorInfo(
  1409. errorKey, errorMessage, errantValue, false);
  1410. recordValidationError(error);
  1411. }
  1412. /**
  1413. * Record an error to the error manager.
  1414. * To avoid confusion between the current and new ErrorManagers,
  1415. * this is the only way to access the setErrorMessageAndValue().
  1416. * @param error The error object.
  1417. * @aribaapi ariba
  1418. */
  1419. public void recordValidationError (AWErrorInfo error)
  1420. {
  1421. Assert.that(error != null, "Cannot record an error with a NULL AWErrorInfo");
  1422. AWErrorManager.AWNewErrorManager errorManager =
  1423. (AWErrorManager.AWNewErrorManager)_page.errorManager();
  1424. errorManager.setErrorMessageAndValue(error);
  1425. }
  1426. /**
  1427. * Record multiple error to the error manager.
  1428. * To avoid confusion between the current and new ErrorManagers,
  1429. * this is the only way to access the setErrorMessageAndValue().
  1430. * @param errors a list of AWErrorInfo objects
  1431. * @aribaapi ariba
  1432. */
  1433. public void recordValidationErrors (List errors)
  1434. {
  1435. for (int i = 0; i < errors.size(); i++) {
  1436. recordValidationError((AWErrorInfo)errors.get(i));
  1437. }
  1438. }
  1439. /**
  1440. * Record an error to the error manager.
  1441. * To avoid confusion between the current and new ErrorManagers,
  1442. * this is the only way to access the setErrorMessageAndValue().
  1443. * @param exception
  1444. * @param errorKey The object that identifies the error. This is typically a string.
  1445. * @param errantValue The unparsable value that the user entered. Since
  1446. the parsing failed, we cannot store this value in
  1447. the field. We stash it away here so we can display
  1448. in the UI later.
  1449. * @aribaapi ariba
  1450. */
  1451. public void recordValidationError (Throwable exception, Object errorKey,
  1452. Object errantValue)
  1453. {
  1454. Assert.that(errorKey != null, "errorKey cannot be null");
  1455. String errorMessage = exception.getLocalizedMessage();
  1456. AWErrorInfo error = new AWErrorInfo(
  1457. errorKey, errorMessage, errantValue, false);
  1458. recordValidationError(error);
  1459. }
  1460. /**
  1461. * Record an error to the error manager.
  1462. * To avoid confusion between the current and new ErrorManagers,
  1463. * this is the only way to access the setErrorMessageAndValue().
  1464. * @param errorKey The object that identifies the error. This is typically a string.
  1465. * @param warningMessage The message that describes the warning.
  1466. * @aribaapi ariba
  1467. */
  1468. public void recordValidationWarning (Object errorKey, String warningMessage)
  1469. {
  1470. Assert.that(errorKey != null, "errorKey cannot be null");
  1471. AWErrorInfo error = new AWErrorInfo(
  1472. errorKey, warningMessage, null, true);
  1473. recordValidationError(error);
  1474. }
  1475. /**
  1476. * Clear an error in the error manager.
  1477. * To avoid confusion between the current and new ErrorManagers,
  1478. * this is the only way to access the clearErrorForKey().
  1479. * @param errorKey
  1480. * @aribaapi ariba
  1481. */
  1482. public void clearValidationError (Object errorKey)
  1483. {
  1484. if (errorKey == null) {
  1485. return;
  1486. }
  1487. AWErrorManager.AWNewErrorManager errorManager =
  1488. (AWErrorManager.AWNewErrorManager)_page.errorManager();
  1489. errorManager.clearErrorForKey(errorKey);
  1490. }
  1491. //////////////////////////////////
  1492. // Localized Java Strings support
  1493. //////////////////////////////////
  1494. public String localizedJavaString (int stringId, String originalString)
  1495. {
  1496. return _componentReference.componentDefinition().localizedJavaString(stringId, originalString, this, _page.resourceManager());
  1497. }
  1498. public AWStringsThunk strings ()
  1499. {
  1500. return resourceManager().strings();
  1501. }
  1502. public String urlForResourceNamed (String resourceName)
  1503. {
  1504. AWRequestContext requestContext = requestContext();
  1505. boolean isMetaTemplateMode = requestContext.isMetaTemplateMode();
  1506. return urlForResourceNamed(resourceName, isMetaTemplateMode);
  1507. }
  1508. public String urlForResourceNamed (String resourceName, boolean useFullUrl)
  1509. {
  1510. return urlForResourceNamed (resourceName, useFullUrl, false);
  1511. }
  1512. public String urlForResourceNamed (String resourceName, boolean useFullUrl, boolean isVersioned)
  1513. {
  1514. AWRequestContext requestContext = requestContext();
  1515. boolean isSecure = useFullUrl ? requestContext.request() != null && requestContext.request().isSecureScheme() : false;
  1516. String url = resourceManager().urlForResourceNamed(resourceName, useFullUrl, isSecure, isVersioned);
  1517. return url;
  1518. }
  1519. /*-----------------------------------------------------------------------
  1520. Localizaton convenience methods
  1521. -----------------------------------------------------------------------*/
  1522. public boolean isBidirectional ()
  1523. {
  1524. return I18NUtil.isBidirectional(preferredLocale());
  1525. }
  1526. public String languageDirection ()
  1527. {
  1528. return I18NUtil.languageDirection(preferredLocale());
  1529. }
  1530. public String languageRight ()
  1531. {
  1532. return I18NUtil.languageRight(preferredLocale());
  1533. }
  1534. public String languageLeft ()
  1535. {
  1536. return I18NUtil.languageLeft(preferredLocale());
  1537. }
  1538. /*----------------------------------------------------------------------
  1539. record and playback
  1540. --------------------------------------------------------------------*/
  1541. /**
  1542. * If any component wants to provide a semantic key different from the
  1543. * default, it should override _debugSemanticKeyInteresting() and _debugSemanticKey()
  1544. * @return whether key is interesting
  1545. */
  1546. protected boolean _debugSemanticKeyInteresting ()
  1547. {
  1548. return _debugPrimaryBinding() == null;
  1549. }
  1550. protected String _debugSemanticKey ()
  1551. {
  1552. String semanticKey = null;
  1553. if (semanticKey == null) {
  1554. AWBinding binding = _debugPrimaryBinding();
  1555. if (binding != null) {
  1556. semanticKey = AWRecordingManager.actionEffectiveKeyPathInComponent(binding, parent());
  1557. }
  1558. else if (semanticKey == null) {
  1559. semanticKey = ClassUtil.stripPackageFromClassName(name());
  1560. }
  1561. }
  1562. return semanticKey;
  1563. }
  1564. protected AWBinding _debugPrimaryBinding ()
  1565. {
  1566. AWBinding primaryBinding = null;
  1567. AWComponentReference componentRef = componentReference();
  1568. if (componentRef != null) {
  1569. primaryBinding = bindingForName(AWBindingNames._awname, false);
  1570. if (primaryBinding == null) {
  1571. primaryBinding = bindingForName(AWBindingNames.action, false);
  1572. if (primaryBinding == null) {
  1573. primaryBinding = bindingForName(AWBindingNames.value, false);
  1574. }
  1575. }
  1576. }
  1577. return primaryBinding;
  1578. }
  1579. /**
  1580. * enhanced implmentation for record & playback:
  1581. * component returns a semantic key based on the following algorithm:
  1582. * If the component reference is interesting (ie, not widgets), then use the primary
  1583. * binding key path such as action or value as the semantic key. Otherwise, ask if
  1584. * the parent is interesting, if yes, return the parent semantic key :: my semantic key.
  1585. * Otherwise, recursively ask the parent to return its semantic key
  1586. */
  1587. protected String _debugCompositeSemanticKey (String bestKeySoFar)
  1588. {
  1589. if (_debugSemanticKeyInteresting() || pageComponent() == this) {
  1590. String myKey = _debugSemanticKey();
  1591. return bestKeySoFar == null ? myKey :
  1592. StringUtil.strcat(_debugSemanticKey(), ":", bestKeySoFar);
  1593. }
  1594. if (bestKeySoFar == null) {
  1595. bestKeySoFar = _debugSemanticKey();
  1596. }
  1597. return parent()._debugCompositeSemanticKey(bestKeySoFar);
  1598. }
  1599. /**
  1600. * Sometimes, the semantic key logging in AWGenericElement is not enough or doesn't work
  1601. * for this particular component. The component can override _debugRecordMapping
  1602. * and return false for _debugRecordingMappingInGenericElement
  1603. * @param requestContext
  1604. * @param component
  1605. */
  1606. protected void _debugRecordMapping (AWRequestContext requestContext, AWComponent component)
  1607. {
  1608. }
  1609. protected boolean _debugRecordMappingInGenericElement ()
  1610. {
  1611. return true;
  1612. }
  1613. /**
  1614. * Override this method to disable wrapping of components in spans when doing componentPathDebugging
  1615. * @return whether component path debugging is allowed for this component
  1616. */
  1617. protected boolean allowComponentPathDebugging ()
  1618. {
  1619. return true;
  1620. }
  1621. /**
  1622. * Override this method to restore the behavior in this page to that of aribaweb-7.
  1623. * This only applies to page-level components
  1624. */
  1625. public boolean requiresPreGlidCompatibility ()
  1626. {
  1627. // all modules are now using modern approach
  1628. return false;
  1629. }
  1630. /**
  1631. * Allows AWSessionValidator to evaluate when the page.ensureAwake is called.
  1632. * Should be overridden by page components that do not require session validation.
  1633. *
  1634. * @return true by default
  1635. */
  1636. protected boolean shouldValidateSession ()
  1637. {
  1638. return true;
  1639. }
  1640. protected void validateSession (AWRequestContext requestContext)
  1641. {
  1642. requestContext.application().assertValidSession(requestContext);
  1643. }
  1644. /**
  1645. Provides verification on the reques