Source Code Cross Referenced for ComponentPageElementImpl.java in  » Library » Tapestry » org » apache » tapestry » internal » structure » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Library » Tapestry » org.apache.tapestry.internal.structure 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        // Copyright 2006, 2007 The Apache Software Foundation
0002:        //
0003:        // Licensed under the Apache License, Version 2.0 (the "License");
0004:        // you may not use this file except in compliance with the License.
0005:        // You may obtain a copy of the License at
0006:        //
0007:        //     http://www.apache.org/licenses/LICENSE-2.0
0008:        //
0009:        // Unless required by applicable law or agreed to in writing, software
0010:        // distributed under the License is distributed on an "AS IS" BASIS,
0011:        // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0012:        // See the License for the specific language governing permissions and
0013:        // limitations under the License.
0014:
0015:        package org.apache.tapestry.internal.structure;
0016:
0017:        import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newCaseInsensitiveMap;
0018:        import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newList;
0019:        import static org.apache.tapestry.ioc.internal.util.Defense.notBlank;
0020:
0021:        import java.util.Iterator;
0022:        import java.util.List;
0023:        import java.util.Locale;
0024:        import java.util.Map;
0025:
0026:        import org.apache.commons.logging.Log;
0027:        import org.apache.tapestry.Binding;
0028:        import org.apache.tapestry.Block;
0029:        import org.apache.tapestry.BlockNotFoundException;
0030:        import org.apache.tapestry.ComponentEventHandler;
0031:        import org.apache.tapestry.ComponentResources;
0032:        import org.apache.tapestry.Link;
0033:        import org.apache.tapestry.MarkupWriter;
0034:        import org.apache.tapestry.Renderable;
0035:        import org.apache.tapestry.dom.Element;
0036:        import org.apache.tapestry.internal.InternalComponentResources;
0037:        import org.apache.tapestry.internal.TapestryInternalUtils;
0038:        import org.apache.tapestry.internal.services.ComponentEventImpl;
0039:        import org.apache.tapestry.internal.services.EventImpl;
0040:        import org.apache.tapestry.internal.services.Instantiator;
0041:        import org.apache.tapestry.internal.util.NotificationEventHandler;
0042:        import org.apache.tapestry.ioc.BaseLocatable;
0043:        import org.apache.tapestry.ioc.Location;
0044:        import org.apache.tapestry.ioc.internal.util.InternalUtils;
0045:        import org.apache.tapestry.ioc.internal.util.TapestryException;
0046:        import org.apache.tapestry.ioc.services.TypeCoercer;
0047:        import org.apache.tapestry.model.ComponentModel;
0048:        import org.apache.tapestry.model.ParameterModel;
0049:        import org.apache.tapestry.runtime.Component;
0050:        import org.apache.tapestry.runtime.ComponentEvent;
0051:        import org.apache.tapestry.runtime.Event;
0052:        import org.apache.tapestry.runtime.PageLifecycleListener;
0053:        import org.apache.tapestry.runtime.RenderCommand;
0054:        import org.apache.tapestry.runtime.RenderQueue;
0055:        import org.apache.tapestry.services.ComponentMessagesSource;
0056:
0057:        /**
0058:         * Implements {@link org.apache.tapestry.internal.structure.PageElement} and
0059:         * {@link org.apache.tapestry.internal.InternalComponentResources}, and represents a component
0060:         * within an overall page. Much of a component page element's behavior is delegated to user code,
0061:         * via a {@link org.apache.tapestry.runtime.Component} instance.
0062:         * <p>
0063:         * Once instantiated, a ComponentPageElementImpl should be registered as a
0064:         * {@link org.apache.tapestry.internal.structure.Page}. This could be done inside the constructors,
0065:         * but that tends to complicate unit tests, so its done by
0066:         * {@link org.apache.tapestry.internal.services.PageElementFactoryImpl}.
0067:         * <p>
0068:         */
0069:        public class ComponentPageElementImpl extends BaseLocatable implements 
0070:                ComponentPageElement, PageLifecycleListener {
0071:            private static final ComponentCallback CONTAINING_PAGE_DID_ATTACH = new ComponentCallback() {
0072:                public void run(Component component) {
0073:                    component.containingPageDidAttach();
0074:                }
0075:            };
0076:
0077:            private static final ComponentCallback CONTAINING_PAGE_DID_DETACH = new ComponentCallback() {
0078:                public void run(Component component) {
0079:                    component.containingPageDidDetach();
0080:                }
0081:            };
0082:
0083:            private static final ComponentCallback CONTAINING_PAGE_DID_LOAD = new ComponentCallback() {
0084:                public void run(Component component) {
0085:                    component.containingPageDidLoad();
0086:                }
0087:            };
0088:
0089:            private static final ComponentCallback POST_RENDER_CLEANUP = new ComponentCallback() {
0090:                public void run(Component component) {
0091:                    component.postRenderCleanup();
0092:                }
0093:            };
0094:
0095:            // For the momement, every component will have a template, even if it consists of
0096:            // just a page element to queue up a BeforeRenderBody phase.
0097:
0098:            private static void pushElements(RenderQueue queue,
0099:                    List<PageElement> list) {
0100:                int count = size(list);
0101:                for (int i = count - 1; i >= 0; i--)
0102:                    queue.push(list.get(i));
0103:            }
0104:
0105:            private static int size(List<?> list) {
0106:                return list == null ? 0 : list.size();
0107:            }
0108:
0109:            private static class RenderPhaseEventHandler implements 
0110:                    ComponentEventHandler {
0111:                private boolean _result = true;
0112:
0113:                private List<RenderCommand> _commands;
0114:
0115:                boolean getResult() {
0116:                    return _result;
0117:                }
0118:
0119:                public boolean handleResult(Object result, Component component,
0120:                        String methodDescription) {
0121:                    if (result instanceof  Boolean) {
0122:                        _result = (Boolean) result;
0123:                        return true; // abort other handler methods
0124:                    }
0125:
0126:                    if (result instanceof  RenderCommand) {
0127:                        RenderCommand command = (RenderCommand) result;
0128:
0129:                        add(command);
0130:
0131:                        return false; // do not abort!
0132:                    }
0133:
0134:                    if (result instanceof  Renderable) {
0135:                        final Renderable renderable = (Renderable) result;
0136:
0137:                        RenderCommand wrapper = new RenderCommand() {
0138:                            public void render(MarkupWriter writer,
0139:                                    RenderQueue queue) {
0140:                                renderable.render(writer);
0141:                            }
0142:                        };
0143:
0144:                        add(wrapper);
0145:
0146:                        return false;
0147:                    }
0148:
0149:                    throw new TapestryException(StructureMessages
0150:                            .wrongEventResultType(methodDescription,
0151:                                    Boolean.class), component, null);
0152:                }
0153:
0154:                private void add(RenderCommand command) {
0155:                    if (_commands == null)
0156:                        _commands = newList();
0157:
0158:                    _commands.add(command);
0159:                }
0160:
0161:                public void queueCommands(RenderQueue queue) {
0162:                    if (_commands == null)
0163:                        return;
0164:
0165:                    for (RenderCommand command : _commands)
0166:                        queue.push(command);
0167:                }
0168:            };
0169:
0170:            private final RenderCommand _afterRender = new RenderCommand() {
0171:                public void render(final MarkupWriter writer, RenderQueue queue) {
0172:                    RenderPhaseEventHandler handler = new RenderPhaseEventHandler();
0173:                    final Event event = new EventImpl(handler);
0174:
0175:                    ComponentCallback callback = new ComponentCallback() {
0176:                        public void run(Component component) {
0177:                            component.afterRender(writer, event);
0178:                        }
0179:                    };
0180:
0181:                    invoke(true, callback);
0182:
0183:                    if (!handler.getResult())
0184:                        queue.push(_beginRender);
0185:
0186:                    handler.queueCommands(queue);
0187:                }
0188:
0189:                @Override
0190:                public String toString() {
0191:                    return phaseToString("AfterRender");
0192:                }
0193:            };
0194:
0195:            private final RenderCommand _afterRenderBody = new RenderCommand() {
0196:                public void render(final MarkupWriter writer, RenderQueue queue) {
0197:                    RenderPhaseEventHandler handler = new RenderPhaseEventHandler();
0198:                    final Event event = new EventImpl(handler);
0199:
0200:                    ComponentCallback callback = new ComponentCallback() {
0201:                        public void run(Component component) {
0202:                            component.afterRenderBody(writer, event);
0203:                        }
0204:                    };
0205:
0206:                    invoke(true, callback);
0207:
0208:                    if (!handler.getResult())
0209:                        queue.push(_beforeRenderBody);
0210:
0211:                    handler.queueCommands(queue);
0212:                }
0213:
0214:                @Override
0215:                public String toString() {
0216:                    return phaseToString("AfterRenderBody");
0217:                }
0218:            };
0219:
0220:            private final RenderCommand _afterRenderTemplate = new RenderCommand() {
0221:                public void render(final MarkupWriter writer,
0222:                        final RenderQueue queue) {
0223:                    RenderPhaseEventHandler handler = new RenderPhaseEventHandler();
0224:                    final Event event = new EventImpl(handler);
0225:
0226:                    ComponentCallback callback = new ComponentCallback() {
0227:                        public void run(Component component) {
0228:                            component.afterRenderTemplate(writer, event);
0229:                        }
0230:                    };
0231:
0232:                    invoke(true, callback);
0233:
0234:                    if (!handler.getResult())
0235:                        queue.push(_beforeRenderTemplate);
0236:
0237:                    handler.queueCommands(queue);
0238:                }
0239:
0240:                @Override
0241:                public String toString() {
0242:                    return phaseToString("AfterRenderTemplate");
0243:                }
0244:            };
0245:
0246:            private final RenderCommand _beforeRenderBody = new RenderCommand() {
0247:                public void render(final MarkupWriter writer, RenderQueue queue) {
0248:                    RenderPhaseEventHandler handler = new RenderPhaseEventHandler();
0249:                    final Event event = new EventImpl(handler);
0250:
0251:                    ComponentCallback callback = new ComponentCallback() {
0252:                        public void run(Component component) {
0253:                            component.beforeRenderBody(writer, event);
0254:                        }
0255:                    };
0256:
0257:                    invoke(false, callback);
0258:
0259:                    queue.push(_afterRenderBody);
0260:
0261:                    if (handler.getResult())
0262:                        pushElements(queue, _body);
0263:
0264:                    handler.queueCommands(queue);
0265:                }
0266:
0267:                @Override
0268:                public String toString() {
0269:                    return phaseToString("BeforeRenderBody");
0270:                }
0271:            };
0272:
0273:            private final RenderCommand _beforeRenderTemplate = new RenderCommand() {
0274:                public void render(final MarkupWriter writer,
0275:                        final RenderQueue queue) {
0276:                    final RenderPhaseEventHandler handler = new RenderPhaseEventHandler();
0277:                    final Event event = new EventImpl(handler);
0278:
0279:                    ComponentCallback callback = new ComponentCallback() {
0280:                        public void run(Component component) {
0281:                            component.beforeRenderTemplate(writer, event);
0282:                        }
0283:                    };
0284:
0285:                    invoke(false, callback);
0286:
0287:                    queue.push(_afterRenderTemplate);
0288:
0289:                    if (handler.getResult())
0290:                        pushElements(queue, _template);
0291:
0292:                    handler.queueCommands(queue);
0293:                }
0294:
0295:                @Override
0296:                public String toString() {
0297:                    return phaseToString("BeforeRenderTemplate");
0298:                }
0299:            };
0300:
0301:            private final RenderCommand _beginRender = new RenderCommand() {
0302:                public void render(final MarkupWriter writer,
0303:                        final RenderQueue queue) {
0304:                    RenderPhaseEventHandler handler = new RenderPhaseEventHandler();
0305:                    final Event event = new EventImpl(handler);
0306:
0307:                    ComponentCallback callback = new ComponentCallback() {
0308:                        public void run(Component component) {
0309:                            component.beginRender(writer, event);
0310:                        }
0311:                    };
0312:
0313:                    invoke(false, callback);
0314:
0315:                    queue.push(_afterRender);
0316:
0317:                    // If the component has no template whatsoever, then a
0318:                    // renderBody element is added as the lone element of the component's template.
0319:                    // So every component will have a non-empty template.
0320:
0321:                    if (handler.getResult())
0322:                        queue.push(_beforeRenderTemplate);
0323:
0324:                    handler.queueCommands(queue);
0325:                }
0326:
0327:                @Override
0328:                public String toString() {
0329:                    return phaseToString("BeginRender");
0330:                }
0331:            };
0332:
0333:            private Map<String, Block> _blocks;
0334:
0335:            private List<PageElement> _body;
0336:
0337:            private Map<String, ComponentPageElement> _children;
0338:
0339:            private final String _elementName;
0340:
0341:            private final RenderCommand _cleanupRender = new RenderCommand() {
0342:                public void render(final MarkupWriter writer, RenderQueue queue) {
0343:                    RenderPhaseEventHandler handler = new RenderPhaseEventHandler();
0344:                    final Event event = new EventImpl(handler);
0345:
0346:                    ComponentCallback callback = new ComponentCallback() {
0347:                        public void run(Component component) {
0348:                            component.cleanupRender(writer, event);
0349:                        }
0350:                    };
0351:
0352:                    invoke(true, callback);
0353:
0354:                    if (handler.getResult()) {
0355:                        _rendering = false;
0356:
0357:                        Element current = writer.getElement();
0358:
0359:                        if (current != _elementAtSetup)
0360:                            throw new TapestryException(StructureMessages
0361:                                    .unbalancedElements(_completeId),
0362:                                    getLocation(), null);
0363:
0364:                        _elementAtSetup = null;
0365:
0366:                        invoke(false, POST_RENDER_CLEANUP);
0367:
0368:                        // NOW and only now the component is done rendering and fully cleaned up. Decrement
0369:                        // the page's dirty count. If the entire render goes well, then the page will be
0370:                        // clean and can be stored into the pool for later reuse.
0371:
0372:                        _page.decrementDirtyCount();
0373:                    } else {
0374:                        queue.push(_setupRender);
0375:                    }
0376:
0377:                    handler.queueCommands(queue);
0378:                }
0379:
0380:                @Override
0381:                public String toString() {
0382:                    return phaseToString("CleanupRender");
0383:                }
0384:            };
0385:
0386:            private final String _completeId;
0387:
0388:            // The user-provided class, with runtime code enhancements. In a component with mixins, this
0389:            // is the component to which the mixins are attached.
0390:            private final Component _coreComponent;
0391:
0392:            private final ComponentMessagesSource _messagesSource;
0393:
0394:            /**
0395:             * Component lifecycle instances for all mixins; the core component is added to this list during
0396:             * page load. This is only used in the case that a component has mixins (in which case, the core
0397:             * component is listed last).
0398:             */
0399:            private List<Component> _components = null;
0400:
0401:            private final ComponentPageElement _container;
0402:
0403:            private final InternalComponentResources _coreResources;
0404:
0405:            private final String _id;
0406:
0407:            private boolean _loaded;
0408:
0409:            /** Map from mixin name to resources for the mixin. Created when first mixin is added. */
0410:            private Map<String, InternalComponentResources> _mixinsByShortName;
0411:
0412:            private final String _nestedId;
0413:
0414:            private final Page _page;
0415:
0416:            private boolean _rendering;
0417:
0418:            private Element _elementAtSetup;
0419:
0420:            private final RenderCommand _setupRender = new RenderCommand() {
0421:                public void render(final MarkupWriter writer, RenderQueue queue) {
0422:                    // TODO: Check for recursive rendering.
0423:
0424:                    _rendering = true;
0425:
0426:                    _elementAtSetup = writer.getElement();
0427:
0428:                    RenderPhaseEventHandler handler = new RenderPhaseEventHandler();
0429:                    final Event event = new EventImpl(handler);
0430:
0431:                    ComponentCallback callback = new ComponentCallback() {
0432:                        public void run(Component component) {
0433:                            component.setupRender(writer, event);
0434:                        }
0435:                    };
0436:
0437:                    invoke(false, callback);
0438:
0439:                    queue.push(_cleanupRender);
0440:
0441:                    if (handler.getResult())
0442:                        queue.push(_beginRender);
0443:
0444:                    handler.queueCommands(queue);
0445:                }
0446:
0447:                @Override
0448:                public String toString() {
0449:                    return phaseToString("SetupRender");
0450:                }
0451:            };
0452:
0453:            // We know that, at the very least, there will be an element to force the component to render
0454:            // its body,
0455:            // so there's no reason to wait to initialize the list.
0456:
0457:            private final List<PageElement> _template = newList();
0458:
0459:            private final TypeCoercer _typeCoercer;
0460:
0461:            /**
0462:             * Constructor for other components embedded within the root component or at deeper levels of
0463:             * the hierarchy.
0464:             * 
0465:             * @param page
0466:             *            ultimately containing this component
0467:             * @param container
0468:             *            component immediately containing this component (may be null for a root component)
0469:             * @param id
0470:             *            unique (within the container) id for this component (may be null for a root
0471:             *            component)
0472:             * @param elementName
0473:             *            the name of the element which represents this component in the template, or null
0474:             *            for &lt;comp&gt; element or a page component
0475:             * @param instantiator
0476:             *            used to create the new component instance and access the component's model
0477:             * @param typeCoercer
0478:             *            used when coercing parameter values
0479:             * @param messagesSource
0480:             *            Provides access to the component's message catalog
0481:             * @param location
0482:             *            location of the element (within a template), used as part of exception reporting
0483:             */
0484:
0485:            public ComponentPageElementImpl(Page page,
0486:                    ComponentPageElement container, String id,
0487:                    String elementName, Instantiator instantiator,
0488:                    TypeCoercer typeCoercer,
0489:                    ComponentMessagesSource messagesSource, Location location) {
0490:                super (location);
0491:
0492:                _page = page;
0493:                _container = container;
0494:                _id = id;
0495:                _elementName = elementName;
0496:                _typeCoercer = typeCoercer;
0497:                _messagesSource = messagesSource;
0498:
0499:                ComponentResources containerResources = container == null ? null
0500:                        : container.getComponentResources();
0501:
0502:                _coreResources = new InternalComponentResourcesImpl(this ,
0503:                        containerResources, instantiator, _typeCoercer,
0504:                        _messagesSource);
0505:
0506:                _coreComponent = _coreResources.getComponent();
0507:
0508:                String pageName = _page.getLogicalName();
0509:
0510:                // A page (really, the root component of a page) does not have a container.
0511:
0512:                if (container == null) {
0513:                    _completeId = pageName;
0514:                    _nestedId = null;
0515:                } else {
0516:                    String caselessId = id.toLowerCase();
0517:
0518:                    String parentNestedId = container.getNestedId();
0519:
0520:                    // The root element has no nested id.
0521:                    // The children of the root element have an id.
0522:
0523:                    if (parentNestedId == null) {
0524:                        _nestedId = caselessId;
0525:                        _completeId = pageName + ":" + caselessId;
0526:                    } else {
0527:                        _nestedId = parentNestedId + "." + caselessId;
0528:                        _completeId = container.getCompleteId() + "."
0529:                                + caselessId;
0530:                    }
0531:                }
0532:            }
0533:
0534:            /**
0535:             * Constructor for the root component of a page.
0536:             */
0537:            public ComponentPageElementImpl(Page page,
0538:                    Instantiator instantiator, TypeCoercer typeCoercer,
0539:                    ComponentMessagesSource messagesSource) {
0540:                this (page, null, null, null, instantiator, typeCoercer,
0541:                        messagesSource, null);
0542:            }
0543:
0544:            public void addEmbeddedElement(ComponentPageElement child) {
0545:                if (_children == null)
0546:                    _children = newCaseInsensitiveMap();
0547:
0548:                String childId = child.getId();
0549:
0550:                ComponentPageElement existing = _children.get(childId);
0551:                if (existing != null)
0552:                    throw new TapestryException(StructureMessages
0553:                            .duplicateChildComponent(this , childId), child,
0554:                            null);
0555:
0556:                _children.put(childId, child);
0557:            }
0558:
0559:            public void addMixin(Instantiator instantiator) {
0560:                if (_mixinsByShortName == null) {
0561:                    _mixinsByShortName = newCaseInsensitiveMap();
0562:                    _components = newList();
0563:                }
0564:
0565:                String mixinClassName = instantiator.getModel()
0566:                        .getComponentClassName();
0567:                String mixinName = TapestryInternalUtils
0568:                        .lastTerm(mixinClassName);
0569:
0570:                InternalComponentResourcesImpl resources = new InternalComponentResourcesImpl(
0571:                        this , _coreResources, instantiator, _typeCoercer,
0572:                        _messagesSource);
0573:
0574:                // TODO: Check for name collision?
0575:
0576:                _mixinsByShortName.put(mixinName, resources);
0577:
0578:                _components.add(resources.getComponent());
0579:            }
0580:
0581:            public void bindParameter(String parameterName, Binding binding) {
0582:                // Maybe should use colon here? Depends on what works best in the template,
0583:                // don't want to lock this out as just
0584:                int dotx = parameterName.lastIndexOf('.');
0585:
0586:                if (dotx > 0) {
0587:                    String mixinName = parameterName.substring(0, dotx);
0588:                    InternalComponentResources mixinResources = InternalUtils
0589:                            .get(_mixinsByShortName, mixinName);
0590:
0591:                    if (mixinResources == null)
0592:                        throw new TapestryException(StructureMessages
0593:                                .missingMixinForParameter(_completeId,
0594:                                        mixinName, parameterName), binding,
0595:                                null);
0596:
0597:                    String simpleName = parameterName.substring(dotx + 1);
0598:
0599:                    mixinResources.bindParameter(simpleName, binding);
0600:                    return;
0601:                }
0602:
0603:                InternalComponentResources informalParameterResources = null;
0604:
0605:                // Does it match a formal parameter name of the core component? That takes precedence
0606:
0607:                if (_coreResources.getComponentModel().getParameterModel(
0608:                        parameterName) != null) {
0609:                    _coreResources.bindParameter(parameterName, binding);
0610:                    return;
0611:                }
0612:
0613:                for (String mixinName : InternalUtils
0614:                        .sortedKeys(_mixinsByShortName)) {
0615:                    InternalComponentResources resources = _mixinsByShortName
0616:                            .get(mixinName);
0617:                    if (resources.getComponentModel().getParameterModel(
0618:                            parameterName) != null) {
0619:                        resources.bindParameter(parameterName, binding);
0620:                        return;
0621:                    }
0622:
0623:                    if (informalParameterResources == null
0624:                            && resources.getComponentModel()
0625:                                    .getSupportsInformalParameters())
0626:                        informalParameterResources = resources;
0627:                }
0628:
0629:                // An informal parameter
0630:
0631:                if (informalParameterResources == null
0632:                        && _coreResources.getComponentModel()
0633:                                .getSupportsInformalParameters())
0634:                    informalParameterResources = _coreResources;
0635:
0636:                // For the moment, informal parameters accumulate in the core component's resources, but
0637:                // that will likely change.
0638:
0639:                if (informalParameterResources != null)
0640:                    informalParameterResources.bindParameter(parameterName,
0641:                            binding);
0642:            }
0643:
0644:            public void addToBody(PageElement element) {
0645:                if (_body == null)
0646:                    _body = newList();
0647:
0648:                _body.add(element);
0649:            }
0650:
0651:            public void addToTemplate(PageElement element) {
0652:                _template.add(element);
0653:            }
0654:
0655:            private void addUnboundParameterNames(String prefix,
0656:                    List<String> unbound, InternalComponentResources resource) {
0657:                ComponentModel model = resource.getComponentModel();
0658:
0659:                for (String name : model.getParameterNames()) {
0660:                    if (resource.isBound(name))
0661:                        continue;
0662:
0663:                    ParameterModel parameterModel = model
0664:                            .getParameterModel(name);
0665:
0666:                    if (parameterModel.isRequired()) {
0667:                        String fullName = prefix == null ? name : prefix + "."
0668:                                + name;
0669:
0670:                        unbound.add(fullName);
0671:                    }
0672:                }
0673:            }
0674:
0675:            public void containingPageDidAttach() {
0676:                invoke(false, CONTAINING_PAGE_DID_ATTACH);
0677:            }
0678:
0679:            public void containingPageDidDetach() {
0680:                invoke(false, CONTAINING_PAGE_DID_DETACH);
0681:            }
0682:
0683:            public void containingPageDidLoad() {
0684:                // If this component has mixins, add the core component to the end of the list, after the
0685:                // mixins.
0686:
0687:                if (_components != null) {
0688:                    List<Component> ordered = newList();
0689:
0690:                    Iterator<Component> i = _components.iterator();
0691:
0692:                    // Add all the normal components to the final list.
0693:
0694:                    while (i.hasNext()) {
0695:                        Component mixin = i.next();
0696:
0697:                        if (mixin.getComponentResources().getComponentModel()
0698:                                .isMixinAfter())
0699:                            continue;
0700:
0701:                        ordered.add(mixin);
0702:
0703:                        // Remove from list, leaving just the late executing mixins
0704:
0705:                        i.remove();
0706:                    }
0707:
0708:                    ordered.add(_coreComponent);
0709:
0710:                    // Add the remaining, late executing mixins
0711:
0712:                    ordered.addAll(_components);
0713:
0714:                    _components = ordered;
0715:                }
0716:
0717:                _loaded = true;
0718:
0719:                // For some parameters, bindings (from defaults) are provided inside the callback method, so
0720:                // that is invoked first, before we check for unbound parameters.
0721:
0722:                invoke(false, CONTAINING_PAGE_DID_LOAD);
0723:
0724:                verifyRequiredParametersAreBound();
0725:            }
0726:
0727:            /**
0728:             * Delegates to the
0729:             * {@link Page#createActionLink(Element, ComponentPageElement, String, boolean, Object[]) the containing page}.
0730:             * Why the extra layer? Trying to avoid some unwanted injection (of LinkFactory, into every
0731:             * component page element).
0732:             */
0733:            public Link createActionLink(String action, boolean forForm,
0734:                    Object... context) {
0735:                return _page.createActionLink(this , action, forForm, context);
0736:            }
0737:
0738:            public Link createPageLink(String pageName, boolean override,
0739:                    Object... context) {
0740:                return _page.createPageLink(pageName, override, context);
0741:            }
0742:
0743:            public void enqueueBeforeRenderBody(RenderQueue queue) {
0744:                // If no body, then no beforeRenderBody or afterRenderBody
0745:
0746:                if (_body != null)
0747:                    queue.push(_beforeRenderBody);
0748:            }
0749:
0750:            public String getCompleteId() {
0751:                return _completeId;
0752:            }
0753:
0754:            public Component getComponent() {
0755:                return _coreComponent;
0756:            }
0757:
0758:            public InternalComponentResources getComponentResources() {
0759:                return _coreResources;
0760:            }
0761:
0762:            public ComponentPageElement getContainerElement() {
0763:                return _container;
0764:            }
0765:
0766:            public Page getContainingPage() {
0767:                return _page;
0768:            }
0769:
0770:            public ComponentPageElement getEmbeddedElement(String embeddedId) {
0771:                ComponentPageElement embeddedElement = InternalUtils.get(
0772:                        _children, embeddedId.toLowerCase());
0773:
0774:                if (embeddedElement == null)
0775:                    throw new TapestryException(StructureMessages
0776:                            .noSuchComponent(this , embeddedId), this , null);
0777:
0778:                return embeddedElement;
0779:            }
0780:
0781:            public Object getFieldChange(String fieldName) {
0782:                return _page.getFieldChange(this , fieldName);
0783:            }
0784:
0785:            public String getId() {
0786:                return _id;
0787:            }
0788:
0789:            public Log getLog() {
0790:                return _coreResources.getLog();
0791:            }
0792:
0793:            public Component getMixinByClassName(String mixinClassName) {
0794:                Component result = null;
0795:
0796:                if (_mixinsByShortName != null) {
0797:                    for (InternalComponentResources resources : _mixinsByShortName
0798:                            .values()) {
0799:                        if (resources.getComponentModel()
0800:                                .getComponentClassName().equals(mixinClassName)) {
0801:                            result = resources.getComponent();
0802:                            break;
0803:                        }
0804:                    }
0805:                }
0806:
0807:                if (result == null)
0808:                    throw new TapestryException(StructureMessages.unknownMixin(
0809:                            _completeId, mixinClassName), getLocation(), null);
0810:
0811:                return result;
0812:            }
0813:
0814:            public String getNestedId() {
0815:                return _nestedId;
0816:            }
0817:
0818:            public boolean handleEvent(ComponentEvent event) {
0819:                // Simple case: no mixins
0820:
0821:                if (_components == null)
0822:                    return _coreComponent.handleComponentEvent(event);
0823:
0824:                // Otherwise, iterate over mixins + core component
0825:
0826:                boolean result = false;
0827:
0828:                for (Component component : _components) {
0829:                    result |= component.handleComponentEvent(event);
0830:
0831:                    if (event.isAborted())
0832:                        break;
0833:                }
0834:
0835:                return result;
0836:            }
0837:
0838:            public boolean hasFieldChange(String fieldName) {
0839:                return getFieldChange(fieldName) != null;
0840:            }
0841:
0842:            /**
0843:             * Invokes a callback on the component instances (the core component plus any mixins).
0844:             * 
0845:             * @param reverse
0846:             *            if true, the callbacks are in the reverse of the normal order (this is associated
0847:             *            with AfterXXX phases)
0848:             * @param callback
0849:             *            the object to receive each component instance
0850:             */
0851:            private void invoke(boolean reverse, ComponentCallback callback) {
0852:                try { // Optimization: In the most general case (just the one component, no mixins)
0853:                    // invoke the callback on the component and be done ... no iterators, no nothing.
0854:
0855:                    if (_components == null) {
0856:                        callback.run(_coreComponent);
0857:                        return;
0858:                    }
0859:
0860:                    Iterator<Component> i = reverse ? InternalUtils
0861:                            .reverseIterator(_components) : _components
0862:                            .iterator();
0863:
0864:                    while (i.hasNext())
0865:                        callback.run(i.next());
0866:                } catch (Exception ex) {
0867:                    throw new TapestryException(ex.getMessage(), getLocation(),
0868:                            ex);
0869:                }
0870:            }
0871:
0872:            public boolean isLoaded() {
0873:                return _loaded;
0874:            }
0875:
0876:            public boolean isRendering() {
0877:                return _rendering;
0878:            }
0879:
0880:            public void persistFieldChange(ComponentResources resources,
0881:                    String fieldName, Object newValue) {
0882:                // While loading the page (i.e., when setting field defaults), ignore these
0883:                // changes.
0884:
0885:                if (_loaded)
0886:                    _page.persistFieldChange(resources, fieldName, newValue);
0887:            }
0888:
0889:            /** Generate a toString() for the inner classes that represent render phases. */
0890:            private String phaseToString(String phaseName) {
0891:                return String.format("%s[%s]", phaseName, _completeId);
0892:            }
0893:
0894:            /** Pushes the SetupRender phase state onto the queue. */
0895:            public final void render(MarkupWriter writer, RenderQueue queue) {
0896:                // TODO: An error if the _render flag is already set (recursive rendering not
0897:                // allowed or advisable).
0898:
0899:                // Once we start rendering, the page is considered dirty, until we cleanup post render.
0900:
0901:                _page.incrementDirtyCount();
0902:
0903:                queue.push(_setupRender);
0904:            }
0905:
0906:            @Override
0907:            public String toString() {
0908:                return String.format("ComponentPageElement[%s]", _completeId);
0909:            }
0910:
0911:            public boolean triggerEvent(String eventType, Object[] context,
0912:                    ComponentEventHandler handler) {
0913:                boolean result = false;
0914:
0915:                // Provide a default handler for when the provided handler is null.
0916:
0917:                if (handler == null)
0918:                    handler = new NotificationEventHandler(eventType,
0919:                            _completeId);
0920:
0921:                ComponentPageElement component = this ;
0922:                String componentId = "";
0923:
0924:                while (component != null) {
0925:                    ComponentEvent event = new ComponentEventImpl(eventType,
0926:                            componentId, context, handler, _typeCoercer);
0927:
0928:                    result |= component.handleEvent(event);
0929:
0930:                    if (event.isAborted())
0931:                        return result;
0932:
0933:                    // On each bubble up, make the event appear to come from the previous component
0934:                    // in which the event was triggered.
0935:
0936:                    componentId = component.getId();
0937:
0938:                    component = component.getContainerElement();
0939:                }
0940:
0941:                return result;
0942:            }
0943:
0944:            private void verifyRequiredParametersAreBound() {
0945:                List<String> unbound = newList();
0946:
0947:                addUnboundParameterNames(null, unbound, _coreResources);
0948:
0949:                for (String name : InternalUtils.sortedKeys(_mixinsByShortName))
0950:                    addUnboundParameterNames(name, unbound, _mixinsByShortName
0951:                            .get(name));
0952:
0953:                if (unbound.isEmpty())
0954:                    return;
0955:
0956:                throw new TapestryException(StructureMessages
0957:                        .missingParameters(unbound, this ), this , null);
0958:            }
0959:
0960:            public Locale getLocale() {
0961:                return _page.getLocale();
0962:            }
0963:
0964:            public String getElementName() {
0965:                return _elementName;
0966:            }
0967:
0968:            public void queueRender(RenderQueue queue) {
0969:                queue.push(this );
0970:            }
0971:
0972:            public Block getBlock(String id) {
0973:                Block result = findBlock(id);
0974:
0975:                if (result == null)
0976:                    throw new BlockNotFoundException(StructureMessages
0977:                            .blockNotFound(_completeId, id), getLocation());
0978:
0979:                return result;
0980:            }
0981:
0982:            public Block findBlock(String id) {
0983:                notBlank(id, "id");
0984:
0985:                return InternalUtils.get(_blocks, id);
0986:            }
0987:
0988:            public void addBlock(String blockId, Block block) {
0989:                if (_blocks == null)
0990:                    _blocks = newCaseInsensitiveMap();
0991:
0992:                if (_blocks.containsKey(blockId))
0993:                    throw new TapestryException(StructureMessages
0994:                            .duplicateBlock(this , blockId), block, null);
0995:
0996:                _blocks.put(blockId, block);
0997:            }
0998:
0999:            public String getDefaultBindingPrefix(String parameterName) {
1000:                int dotx = parameterName.lastIndexOf('.');
1001:
1002:                if (dotx > 0) {
1003:                    String mixinName = parameterName.substring(0, dotx);
1004:                    InternalComponentResources mixinResources = InternalUtils
1005:                            .get(_mixinsByShortName, mixinName);
1006:
1007:                    if (mixinResources == null)
1008:                        throw new TapestryException(StructureMessages
1009:                                .missingMixinForParameter(_completeId,
1010:                                        mixinName, parameterName), null, null);
1011:
1012:                    String simpleName = parameterName.substring(dotx + 1);
1013:
1014:                    ParameterModel pm = mixinResources.getComponentModel()
1015:                            .getParameterModel(simpleName);
1016:
1017:                    return pm != null ? pm.getDefaultBindingPrefix() : null;
1018:                }
1019:
1020:                // A formal parameter of the core component?
1021:
1022:                ParameterModel pm = _coreResources.getComponentModel()
1023:                        .getParameterModel(parameterName);
1024:
1025:                if (pm != null)
1026:                    return pm.getDefaultBindingPrefix();
1027:
1028:                // Search for mixin that it is a formal parameter of
1029:
1030:                for (String mixinName : InternalUtils
1031:                        .sortedKeys(_mixinsByShortName)) {
1032:                    InternalComponentResources resources = _mixinsByShortName
1033:                            .get(mixinName);
1034:
1035:                    pm = resources.getComponentModel().getParameterModel(
1036:                            parameterName);
1037:
1038:                    if (pm != null)
1039:                        return pm.getDefaultBindingPrefix();
1040:                }
1041:
1042:                // Not a formal parameter of the core component or any mixin.
1043:
1044:                return null;
1045:            }
1046:
1047:            public String getPageName() {
1048:                return _page.getLogicalName();
1049:            }
1050:
1051:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.