PageRenderTime 55ms CodeModel.GetById 21ms 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

Large files files are truncated, but you can click here to view the full file

  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

Large files files are truncated, but you can click here to view the full file