Source Code Cross Referenced for SetNestedPropertiesRule.java in  » Library » Apache-commons-digester-1.8-src » org » apache » commons » digester » 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 » Apache commons digester 1.8 src » org.apache.commons.digester 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /* $Id: SetNestedPropertiesRule.java 472836 2006-11-09 10:06:56Z skitching $
002:         *
003:         * Licensed to the Apache Software Foundation (ASF) under one or more
004:         * contributor license agreements.  See the NOTICE file distributed with
005:         * this work for additional information regarding copyright ownership.
006:         * The ASF licenses this file to You under the Apache License, Version 2.0
007:         * (the "License"); you may not use this file except in compliance with
008:         * the License.  You may obtain a copy of the License at
009:         * 
010:         *      http://www.apache.org/licenses/LICENSE-2.0
011:         * 
012:         * Unless required by applicable law or agreed to in writing, software
013:         * distributed under the License is distributed on an "AS IS" BASIS,
014:         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015:         * See the License for the specific language governing permissions and
016:         * limitations under the License.
017:         */
018:
019:        package org.apache.commons.digester;
020:
021:        import java.util.List;
022:        import java.util.LinkedList;
023:        import java.util.ArrayList;
024:        import java.util.HashMap;
025:        import java.beans.PropertyDescriptor;
026:
027:        import org.apache.commons.beanutils.BeanUtils;
028:        import org.apache.commons.beanutils.DynaBean;
029:        import org.apache.commons.beanutils.DynaProperty;
030:        import org.apache.commons.beanutils.PropertyUtils;
031:
032:        import org.xml.sax.Attributes;
033:
034:        import org.apache.commons.logging.Log;
035:
036:        /**
037:         * <p>Rule implementation that sets properties on the object at the top of the
038:         * stack, based on child elements with names matching properties on that 
039:         * object.</p>
040:         *
041:         * <p>Example input that can be processed by this rule:</p>
042:         * <pre>
043:         *   [widget]
044:         *    [height]7[/height]
045:         *    [width]8[/width]
046:         *    [label]Hello, world[/label]
047:         *   [/widget]
048:         * </pre>
049:         *
050:         * <p>For each child element of [widget], a corresponding setter method is 
051:         * located on the object on the top of the digester stack, the body text of
052:         * the child element is converted to the type specified for the (sole) 
053:         * parameter to the setter method, then the setter method is invoked.</p>
054:         *
055:         * <p>This rule supports custom mapping of xml element names to property names.
056:         * The default mapping for particular elements can be overridden by using 
057:         * {@link #SetNestedPropertiesRule(String[] elementNames,
058:         *                                 String[] propertyNames)}.
059:         * This allows child elements to be mapped to properties with different names.
060:         * Certain elements can also be marked to be ignored.</p>
061:         *
062:         * <p>A very similar effect can be achieved using a combination of the 
063:         * <code>BeanPropertySetterRule</code> and the <code>ExtendedBaseRules</code> 
064:         * rules manager; this <code>Rule</code>, however, works fine with the default 
065:         * <code>RulesBase</code> rules manager.</p>
066:         *
067:         * <p>Note that this rule is designed to be used to set only "primitive"
068:         * bean properties, eg String, int, boolean. If some of the child xml elements
069:         * match ObjectCreateRule rules (ie cause objects to be created) then you must
070:         * use one of the more complex constructors to this rule to explicitly skip
071:         * processing of that xml element, and define a SetNextRule (or equivalent) to
072:         * handle assigning the child object to the appropriate property instead.</p>
073:         *
074:         * <p><b>Implementation Notes</b></p>
075:         *
076:         * <p>This class works by creating its own simple Rules implementation. When
077:         * begin is invoked on this rule, the digester's current rules object is
078:         * replaced by a custom one. When end is invoked for this rule, the original
079:         * rules object is restored. The digester rules objects therefore behave in
080:         * a stack-like manner.</p>
081:         *
082:         * <p>For each child element encountered, the custom Rules implementation
083:         * ensures that a special AnyChildRule instance is included in the matches 
084:         * returned to the digester, and it is this rule instance that is responsible 
085:         * for setting the appropriate property on the target object (if such a property 
086:         * exists). The effect is therefore like a "trailing wildcard pattern". The 
087:         * custom Rules implementation also returns the matches provided by the 
088:         * underlying Rules implementation for the same pattern, so other rules
089:         * are not "disabled" during processing of a SetNestedPropertiesRule.</p> 
090:         *
091:         * <p>TODO: Optimise this class. Currently, each time begin is called,
092:         * new AnyChildRules and AnyChildRule objects are created. It should be
093:         * possible to cache these in normal use (though watch out for when a rule
094:         * instance is invoked re-entrantly!).</p>
095:         *
096:         * @since 1.6
097:         */
098:
099:        public class SetNestedPropertiesRule extends Rule {
100:
101:            private Log log = null;
102:
103:            private boolean trimData = true;
104:            private boolean allowUnknownChildElements = false;
105:
106:            private HashMap elementNames = new HashMap();
107:
108:            // ----------------------------------------------------------- Constructors
109:
110:            /**
111:             * Base constructor, which maps every child element into a bean property
112:             * with the same name as the xml element.
113:             *
114:             * <p>It is an error if a child xml element exists but the target java 
115:             * bean has no such property (unless setAllowUnknownChildElements has been
116:             * set to true).</p>
117:             */
118:            public SetNestedPropertiesRule() {
119:                // nothing to set up 
120:            }
121:
122:            /** 
123:             * <p>Convenience constructor which overrides the default mappings for 
124:             * just one property.</p>
125:             *
126:             * <p>For details about how this works, see
127:             * {@link #SetNestedPropertiesRule(String[] elementNames, 
128:             * String[] propertyNames)}.</p>
129:             *
130:             * @param elementName is the child xml element to match 
131:             * @param propertyName is the java bean property to be assigned the value 
132:             * of the specified xml element. This may be null, in which case the 
133:             * specified xml element will be ignored.
134:             */
135:            public SetNestedPropertiesRule(String elementName,
136:                    String propertyName) {
137:                elementNames.put(elementName, propertyName);
138:            }
139:
140:            /** 
141:             * <p>Constructor which allows element->property mapping to be overridden.
142:             * </p>
143:             *
144:             * <p>Two arrays are passed in. One contains xml element names and the 
145:             * other java bean property names. The element name / property name pairs
146:             * are matched by position; in order words, the first string in the element
147:             * name array corresponds to the first string in the property name array 
148:             * and so on.</p>
149:             *
150:             * <p>If a property name is null or the xml element name has no matching
151:             * property name due to the arrays being of different lengths then this
152:             * indicates that the xml element should be ignored.</p>
153:             * 
154:             * <h5>Example One</h5>
155:             * <p> The following constructs a rule that maps the <code>alt-city</code>
156:             * element to the <code>city</code> property and the <code>alt-state</code>
157:             * to the <code>state</code> property. All other child elements are mapped
158:             * as usual using exact name matching.
159:             * <code><pre>
160:             *      SetNestedPropertiesRule(
161:             *                new String[] {"alt-city", "alt-state"}, 
162:             *                new String[] {"city", "state"});
163:             * </pre></code>
164:             * </p>
165:             *
166:             * <h5>Example Two</h5>
167:             * <p> The following constructs a rule that maps the <code>class</code>
168:             * xml element to the <code>className</code> property. The xml element 
169:             * <code>ignore-me</code> is not mapped, ie is ignored. All other elements 
170:             * are mapped as usual using exact name matching.
171:             * <code><pre>
172:             *      SetPropertiesRule(
173:             *                new String[] {"class", "ignore-me"}, 
174:             *                new String[] {"className"});
175:             * </pre></code>
176:             * </p>
177:             *
178:             * @param elementNames names of elements to map
179:             * @param propertyNames names of properties mapped to
180:             */
181:            public SetNestedPropertiesRule(String[] elementNames,
182:                    String[] propertyNames) {
183:                for (int i = 0, size = elementNames.length; i < size; i++) {
184:                    String propName = null;
185:                    if (i < propertyNames.length) {
186:                        propName = propertyNames[i];
187:                    }
188:
189:                    this .elementNames.put(elementNames[i], propName);
190:                }
191:            }
192:
193:            // --------------------------------------------------------- Public Methods
194:
195:            /** Invoked when rule is added to digester. */
196:            public void setDigester(Digester digester) {
197:                super .setDigester(digester);
198:                log = digester.getLogger();
199:            }
200:
201:            /**
202:             * When set to true, any text within child elements will have leading
203:             * and trailing whitespace removed before assignment to the target
204:             * object. The default value for this attribute is true.
205:             */
206:            public void setTrimData(boolean trimData) {
207:                this .trimData = trimData;
208:            }
209:
210:            /** See {@link #setTrimData}. */
211:            public boolean getTrimData() {
212:                return trimData;
213:            }
214:
215:            /**
216:             * Determines whether an error is reported when a nested element is
217:             * encountered for which there is no corresponding property-setter
218:             * method.
219:             * <p>
220:             * When set to false, any child element for which there is no
221:             * corresponding object property will cause an error to be reported.
222:             * <p>
223:             * When set to true, any child element for which there is no
224:             * corresponding object property will simply be ignored.
225:             * <p>
226:             * The default value of this attribute is false (unknown child elements
227:             * are not allowed).
228:             */
229:            public void setAllowUnknownChildElements(
230:                    boolean allowUnknownChildElements) {
231:                this .allowUnknownChildElements = allowUnknownChildElements;
232:            }
233:
234:            /** See {@link #setAllowUnknownChildElements}. */
235:            public boolean getAllowUnknownChildElements() {
236:                return allowUnknownChildElements;
237:            }
238:
239:            /**
240:             * Process the beginning of this element.
241:             *
242:             * @param namespace is the namespace this attribute is in, or null
243:             * @param name is the name of the current xml element
244:             * @param attributes is the attribute list of this element
245:             */
246:            public void begin(String namespace, String name,
247:                    Attributes attributes) throws Exception {
248:                Rules oldRules = digester.getRules();
249:                AnyChildRule anyChildRule = new AnyChildRule();
250:                anyChildRule.setDigester(digester);
251:                AnyChildRules newRules = new AnyChildRules(anyChildRule);
252:                newRules.init(digester.getMatch() + "/", oldRules);
253:                digester.setRules(newRules);
254:            }
255:
256:            /**
257:             * This is only invoked after all child elements have been processed,
258:             * so we can remove the custom Rules object that does the 
259:             * child-element-matching.
260:             */
261:            public void body(String bodyText) throws Exception {
262:                AnyChildRules newRules = (AnyChildRules) digester.getRules();
263:                digester.setRules(newRules.getOldRules());
264:            }
265:
266:            /**
267:             * Add an additional custom xml-element -> property mapping.
268:             * <p>
269:             * This is primarily intended to be used from the xml rules module
270:             * (as it is not possible there to pass the necessary parameters to the
271:             * constructor for this class). However it is valid to use this method
272:             * directly if desired.
273:             */
274:            public void addAlias(String elementName, String propertyName) {
275:                elementNames.put(elementName, propertyName);
276:            }
277:
278:            /**
279:             * Render a printable version of this Rule.
280:             */
281:            public String toString() {
282:                StringBuffer sb = new StringBuffer("SetNestedPropertiesRule[");
283:                sb.append("allowUnknownChildElements=");
284:                sb.append(allowUnknownChildElements);
285:                sb.append(", trimData=");
286:                sb.append(trimData);
287:                sb.append(", elementNames=");
288:                sb.append(elementNames);
289:                sb.append("]");
290:                return sb.toString();
291:            }
292:
293:            //----------------------------------------- local classes 
294:
295:            /** Private Rules implementation */
296:            private class AnyChildRules implements  Rules {
297:                private String matchPrefix = null;
298:                private Rules decoratedRules = null;
299:
300:                private ArrayList rules = new ArrayList(1);
301:                private AnyChildRule rule;
302:
303:                public AnyChildRules(AnyChildRule rule) {
304:                    this .rule = rule;
305:                    rules.add(rule);
306:                }
307:
308:                public Digester getDigester() {
309:                    return null;
310:                }
311:
312:                public void setDigester(Digester digester) {
313:                }
314:
315:                public String getNamespaceURI() {
316:                    return null;
317:                }
318:
319:                public void setNamespaceURI(String namespaceURI) {
320:                }
321:
322:                public void add(String pattern, Rule rule) {
323:                }
324:
325:                public void clear() {
326:                }
327:
328:                public List match(String matchPath) {
329:                    return match(null, matchPath);
330:                }
331:
332:                public List match(String namespaceURI, String matchPath) {
333:                    List match = decoratedRules.match(namespaceURI, matchPath);
334:
335:                    if ((matchPath.startsWith(matchPrefix))
336:                            && (matchPath.indexOf('/', matchPrefix.length()) == -1)) {
337:
338:                        // The current element is a direct child of the element
339:                        // specified in the init method, so we want to ensure that
340:                        // the rule passed to this object's constructor is included
341:                        // in the returned list of matching rules.
342:
343:                        if ((match == null || match.size() == 0)) {
344:                            // The "real" rules class doesn't have any matches for
345:                            // the specified path, so we return a list containing
346:                            // just one rule: the one passed to this object's
347:                            // constructor.
348:                            return rules;
349:                        } else {
350:                            // The "real" rules class has rules that match the current
351:                            // node, so we return this list *plus* the rule passed to
352:                            // this object's constructor.
353:                            //
354:                            // It might not be safe to modify the returned list,
355:                            // so clone it first.
356:                            LinkedList newMatch = new LinkedList(match);
357:                            newMatch.addLast(rule);
358:                            return newMatch;
359:                        }
360:                    } else {
361:                        return match;
362:                    }
363:                }
364:
365:                public List rules() {
366:                    // This is not actually expected to be called during normal
367:                    // processing.
368:                    //
369:                    // There is only one known case where this is called; when a rule
370:                    // returned from AnyChildRules.match is invoked and throws a
371:                    // SAXException then method Digester.endDocument will be called
372:                    // without having "uninstalled" the AnyChildRules ionstance. That
373:                    // method attempts to invoke the "finish" method for every Rule
374:                    // instance - and thus needs to call rules() on its Rules object,
375:                    // which is this one. Actually, java 1.5 and 1.6beta2 have a
376:                    // bug in their xml implementation such that endDocument is not 
377:                    // called after a SAXException, but other parsers (eg Aelfred)
378:                    // do call endDocument. Here, we therefore need to return the
379:                    // rules registered with the underlying Rules object.
380:                    log.debug("AnyChildRules.rules invoked.");
381:                    return decoratedRules.rules();
382:                }
383:
384:                public void init(String prefix, Rules rules) {
385:                    matchPrefix = prefix;
386:                    decoratedRules = rules;
387:                }
388:
389:                public Rules getOldRules() {
390:                    return decoratedRules;
391:                }
392:            }
393:
394:            private class AnyChildRule extends Rule {
395:                private String currChildNamespaceURI = null;
396:                private String currChildElementName = null;
397:
398:                public void begin(String namespaceURI, String name,
399:                        Attributes attributes) throws Exception {
400:
401:                    currChildNamespaceURI = namespaceURI;
402:                    currChildElementName = name;
403:                }
404:
405:                public void body(String value) throws Exception {
406:                    String propName = currChildElementName;
407:                    if (elementNames.containsKey(currChildElementName)) {
408:                        // overide propName
409:                        propName = (String) elementNames
410:                                .get(currChildElementName);
411:                        if (propName == null) {
412:                            // user wants us to ignore this element
413:                            return;
414:                        }
415:                    }
416:
417:                    boolean debug = log.isDebugEnabled();
418:
419:                    if (debug) {
420:                        log.debug("[SetNestedPropertiesRule]{" + digester.match
421:                                + "} Setting property '" + propName + "' to '"
422:                                + value + "'");
423:                    }
424:
425:                    // Populate the corresponding properties of the top object
426:                    Object top = digester.peek();
427:                    if (debug) {
428:                        if (top != null) {
429:                            log.debug("[SetNestedPropertiesRule]{"
430:                                    + digester.match + "} Set "
431:                                    + top.getClass().getName() + " properties");
432:                        } else {
433:                            log.debug("[SetPropertiesRule]{" + digester.match
434:                                    + "} Set NULL properties");
435:                        }
436:                    }
437:
438:                    if (trimData) {
439:                        value = value.trim();
440:                    }
441:
442:                    if (!allowUnknownChildElements) {
443:                        // Force an exception if the property does not exist
444:                        // (BeanUtils.setProperty() silently returns in this case)
445:                        if (top instanceof  DynaBean) {
446:                            DynaProperty desc = ((DynaBean) top).getDynaClass()
447:                                    .getDynaProperty(propName);
448:                            if (desc == null) {
449:                                throw new NoSuchMethodException(
450:                                        "Bean has no property named "
451:                                                + propName);
452:                            }
453:                        } else /* this is a standard JavaBean */{
454:                            PropertyDescriptor desc = PropertyUtils
455:                                    .getPropertyDescriptor(top, propName);
456:                            if (desc == null) {
457:                                throw new NoSuchMethodException(
458:                                        "Bean has no property named "
459:                                                + propName);
460:                            }
461:                        }
462:                    }
463:
464:                    try {
465:                        BeanUtils.setProperty(top, propName, value);
466:                    } catch (NullPointerException e) {
467:                        log.error("NullPointerException: " + "top=" + top
468:                                + ",propName=" + propName + ",value=" + value
469:                                + "!");
470:                        throw e;
471:                    }
472:                }
473:
474:                public void end(String namespace, String name) throws Exception {
475:                    currChildElementName = null;
476:                }
477:            }
478:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.