Source Code Cross Referenced for JDOMConfigurationProxy.java in  » Inversion-of-Control » carbon » org » sape » carbon » core » config » format » jdom » 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 » Inversion of Control » carbon » org.sape.carbon.core.config.format.jdom 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*
002:         * The contents of this file are subject to the Sapient Public License
003:         * Version 1.0 (the "License"); you may not use this file except in compliance
004:         * with the License. You may obtain a copy of the License at
005:         * http://carbon.sf.net/License.html.
006:         *
007:         * Software distributed under the License is distributed on an "AS IS" basis,
008:         * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
009:         * the specific language governing rights and limitations under the License.
010:         *
011:         * The Original Code is The Carbon Component Framework.
012:         *
013:         * The Initial Developer of the Original Code is Sapient Corporation
014:         *
015:         * Copyright (C) 2003 Sapient Corporation. All Rights Reserved.
016:         */
017:
018:        package org.sape.carbon.core.config.format.jdom;
019:
020:        import java.beans.IndexedPropertyDescriptor;
021:        import java.beans.IntrospectionException;
022:        import java.beans.Introspector;
023:        import java.beans.PropertyDescriptor;
024:        import java.lang.reflect.Array;
025:        import java.lang.reflect.Proxy;
026:        import java.util.HashMap;
027:        import java.util.Iterator;
028:        import java.util.List;
029:        import java.util.Map;
030:
031:        import org.sape.carbon.core.config.Config;
032:        import org.sape.carbon.core.config.Configuration;
033:        import org.sape.carbon.core.config.InvalidConfigurationException;
034:        import org.sape.carbon.core.config.format.AbstractConfigurationProxy;
035:        import org.sape.carbon.core.config.format.ConfigurationFormatException;
036:        import org.sape.carbon.core.config.node.Node;
037:        import org.sape.carbon.core.config.type.ConfigurationTypeException;
038:        import org.sape.carbon.core.config.type.ConfigurationTypeService;
039:        import org.sape.carbon.core.config.type.ConfigurationTypeServiceFactory;
040:        import org.sape.carbon.core.exception.IllegalStateException;
041:        import org.sape.carbon.core.exception.InvalidParameterException;
042:        import org.sape.carbon.core.util.string.StringUtil;
043:
044:        import org.jdom.Document;
045:        import org.jdom.Element;
046:
047:        /**
048:         * <p>This is an extension of an <code>AbstractConfigurationProxy</code>
049:         * that is backed by a JDOM Document data structure.
050:         * </p>
051:         *
052:         * Copyright 2002 Sapient
053:         * @since carbon 1.0
054:         * @author Greg Hinkle, February 2002
055:         * @author Douglas Voet
056:         * @version $Revision: 1.52 $($Author: dvoet $ / $Date: 2003/09/24 14:09:55 $)
057:         */
058:        public class JDOMConfigurationProxy extends AbstractConfigurationProxy {
059:            /** Holds the type service for configuration. */
060:            protected final ConfigurationTypeService typeService = ConfigurationTypeServiceFactory
061:                    .getInstance();
062:
063:            /**
064:             * Map for caching previously accessed attributes
065:             * @since carbon 1.1
066:             */
067:            protected Map attributeCache = new HashMap();
068:
069:            /** Key attribute for a map element. */
070:            public static final String MAP_KEY_ATTRIBUTE = "Key";
071:
072:            /**
073:             * Constructs a JDOMConfigurationProxy for the specified
074:             * class type. This class only supports subclasses of the
075:             * {@link org.sape.carbon.core.config.Configuration} object.
076:             * @param configurationClass The type of Configuration object that should be
077:             *  returned
078:             */
079:            public JDOMConfigurationProxy(Class configurationClass) {
080:                super (configurationClass);
081:            }
082:
083:            /**
084:             * Constructs a JDOMConfigurationProxy for the specified
085:             * class type. This class only supports subclasses of the
086:             * {@link org.sape.carbon.core.config.Configuration} object.
087:             * The supplied document is used as the backing store for
088:             * this configuration object's data.
089:             * @param document The JDOM Document object representing a configurations
090:             *   data
091:             * @param configurationClass The type of configuration to be implemented
092:             */
093:            public JDOMConfigurationProxy(Document document,
094:                    Class configurationClass) {
095:
096:                super (document, document.getRootElement(), configurationClass);
097:            }
098:
099:            /**
100:             * Constructs a JDOMConfigurationProxy for the specified
101:             * class type. This class only supports subclasses of the
102:             * {@link org.sape.carbon.core.config.Configuration} object.
103:             * The supplied Element is used as the backing datastore
104:             * for this configuration object and it's document object
105:             * will be modified when this object is modified.
106:             * @param element The JDOM element object representing to root node
107:             *  of this configuration object
108:             * @param configurationClass The type of configuration object that will be
109:             *   implemented
110:             */
111:            public JDOMConfigurationProxy(Element element,
112:                    Class configurationClass) {
113:
114:                super (null, element, configurationClass);
115:            }
116:
117:            /**
118:             * <P>This method overrides the base proxy's <code>toString</code>
119:             * implementation so that useful data is printed out when toString is
120:             * called.
121:             * </P>
122:             *
123:             * @param proxy the proxy to print
124:             * @return a string representation of this configuration object
125:             */
126:            public String proxyToString(Object proxy) {
127:                return this .getDocumentType().getName() + " ["
128:                        + this .getConfigurationName() + "]";
129:            }
130:
131:            /**
132:             * <P>This method clones this Configuration object through a deep-copy of
133:             * it's underlying datastructure. The resulting configuration object is
134:             * a new object and can be altered without affecting the original.</P>
135:             *
136:             * @return Object the cloned configuration object
137:             */
138:            public Object clone() {
139:
140:                JDOMConfigurationProxy proxy = null;
141:
142:                if (this .document == null) {
143:                    // Expected for nested configurations
144:                    Element newElement = (Element) super .element.clone();
145:
146:                    // Manually construct the invocation handler and rewrap it.
147:                    // This is done seperately because "newProxy()" would only use
148:                    // the root element and would loose document information such
149:                    // as the encoding type.
150:                    proxy = new JDOMConfigurationProxy(newElement, this 
151:                            .getDocumentType());
152:
153:                } else {
154:                    Document doc = (Document) this .document.clone();
155:
156:                    // Manually construct the invocation handler and rewrap it.
157:                    // This is done seperately because "newProxy()" would only use
158:                    // the root element and would loose document information such
159:                    // as the encoding type.
160:                    proxy = new JDOMConfigurationProxy(doc, this 
161:                            .getDocumentType());
162:                }
163:
164:                proxy.setConfigurationName(this .getConfigurationName());
165:
166:                Configuration config = (Configuration) Proxy.newProxyInstance(
167:                        this .getClass().getClassLoader(), new Class[] { this 
168:                                .getDocumentType() }, proxy);
169:
170:                return config;
171:            }
172:
173:            /**
174:             * @inherit
175:             * @see AbstractConfigurationProxy#lookupAttribute
176:             */
177:            public Object lookupAttribute(String attributeName, Class returnType) {
178:                Object attributeValue = this .attributeCache.get(attributeName);
179:                if (attributeValue == null) {
180:
181:                    try {
182:                        // lookup the element in the document
183:                        Element childElement = this .element
184:                                .getChild(attributeName);
185:
186:                        // format and return the element
187:                        attributeValue = formatElement(childElement,
188:                                attributeName, returnType);
189:
190:                        if (this .typeService.isCacheableType(returnType)) {
191:                            this .attributeCache.put(attributeName,
192:                                    attributeValue);
193:                        }
194:
195:                    } catch (ConfigurationFormatException cfe) {
196:                        throw new InvalidConfigurationException(
197:                                this .getClass(), this .getConfigurationName(),
198:                                attributeName,
199:                                "Format error on configuration value.", cfe);
200:                    }
201:                }
202:
203:                return attributeValue;
204:            }
205:
206:            /**
207:             * <P>Retrieves an array of objects of the specified type that have
208:             * the specified name. This might get a list of dependent objects or
209:             * a list of string values.</P>
210:             *
211:             * @param attributeName the name of the array to retrieve
212:             * @param componentType the type of the objects that should be retrieved
213:             *  within the array
214:             * @return an array of objects that match the name and type specified
215:             */
216:            public Object getArray(String attributeName, Class componentType) {
217:
218:                Object attributeArray = this .attributeCache.get(attributeName);
219:                if (attributeArray == null) {
220:                    Element collectionElement = getCollectionElement(
221:                            attributeName, Array.class);
222:
223:                    List list = collectionElement.getChildren(attributeName);
224:                    int length = list.size();
225:
226:                    attributeArray = Array.newInstance(componentType, length);
227:
228:                    try {
229:                        for (int i = 0; i < length; i++) {
230:                            // get the next element
231:                            Element element = (Element) list.get(i);
232:
233:                            // format it
234:                            Object attribute = formatContainedElement(element,
235:                                    attributeName, String.valueOf(i),
236:                                    componentType);
237:
238:                            // add it to the array
239:                            Array.set(attributeArray, i, attribute);
240:                        }
241:                        if (this .typeService.isCacheableType(componentType)) {
242:                            this .attributeCache.put(attributeName,
243:                                    attributeArray);
244:                        }
245:                    } catch (ConfigurationFormatException cfe) {
246:                        throw new InvalidConfigurationException(
247:                                this .getClass(), this .getConfigurationName(),
248:                                attributeName,
249:                                "Format error on configuration value.", cfe);
250:                    }
251:                }
252:                return attributeArray;
253:            }
254:
255:            /**
256:             * @inherit
257:             * @see AbstractConfigurationProxy#getMap
258:             */
259:            public Map getMap(String tagName, Class contentType) {
260:
261:                Map attributeMap = (Map) this .attributeCache.get(tagName);
262:                if (attributeMap == null) {
263:
264:                    Element collectionElement = getCollectionElement(tagName,
265:                            Map.class);
266:
267:                    List list = collectionElement.getChildren(tagName);
268:                    int length = list.size();
269:
270:                    attributeMap = new HashMap();
271:
272:                    try {
273:                        for (int i = 0; i < length; i++) {
274:                            // get the next element
275:                            Element element = (Element) list.get(i);
276:                            String unformattedKey = element
277:                                    .getAttributeValue(MAP_KEY_ATTRIBUTE);
278:
279:                            if (unformattedKey == null) {
280:                                throw new InvalidConfigurationException(
281:                                        this .getClass(),
282:                                        this .getConfigurationName(),
283:                                        tagName,
284:                                        "All map entries in configuration must "
285:                                                + "have a valid [Key] attribute. (And "
286:                                                + "its case sensitive)");
287:                            }
288:
289:                            // format key as a String
290:                            // at this point, this is done only so that token 
291:                            // replacement will be performed for keys, if in the
292:                            // future, we want to support key objects other than 
293:                            // Strings, replace the hard coded String.class with the
294:                            // appropriate Class object and the type service will do
295:                            // the rest
296:                            String key = (String) this .typeService.toObject(
297:                                    String.class, unformattedKey);
298:
299:                            // format it
300:                            Object attribute = formatContainedElement(element,
301:                                    tagName, key, contentType);
302:
303:                            // add it to the map
304:                            if (this .typeService.isCacheableType(contentType)) {
305:                                attributeMap.put(key, attribute);
306:                            }
307:                        }
308:                    } catch (NullPointerException npe) {
309:                        throw new InvalidConfigurationException(
310:                                this .getClass(), this .getConfigurationName(),
311:                                tagName,
312:                                "Map tags must have keys specified by the "
313:                                        + MAP_KEY_ATTRIBUTE + " attribute.",
314:                                npe);
315:                    } catch (ConfigurationFormatException cfe) {
316:
317:                        throw new InvalidConfigurationException(
318:                                this .getClass(), this .getConfigurationName(),
319:                                tagName,
320:                                "Format error on configuration value.", cfe);
321:                    } catch (ConfigurationTypeException cte) {
322:
323:                        throw new InvalidConfigurationException(
324:                                this .getClass(), this .getConfigurationName(),
325:                                tagName, "Format error on configuration key.",
326:                                cte);
327:                    }
328:                    this .attributeCache.put(tagName, attributeMap);
329:                }
330:                return attributeMap;
331:            }
332:
333:            /**
334:             * @inherit
335:             * @see AbstractConfigurationProxy#alterAttribute
336:             */
337:            public void alterAttribute(String attributeName,
338:                    Class attributeType, Object newValue) {
339:
340:                // clear the cached value
341:                this .attributeCache.remove(attributeName);
342:
343:                if (attributeType.isArray()) {
344:
345:                    // 1) Remove all old items from this array
346:                    Element collectionElement = getCollectionElement(
347:                            attributeName, attributeType);
348:
349:                    collectionElement.removeChildren(attributeName);
350:
351:                    if (newValue != null) {
352:                        // 2) Add all new items as entities
353:                        // using reflection here because we can't cast newValue into
354:                        // an Object[] because newValue may be an array of primitives
355:                        for (int i = 0; i < Array.getLength(newValue); i++) {
356:                            Object obj = Array.get(newValue, i);
357:                            addAttribute(collectionElement, attributeName,
358:                                    attributeType.getComponentType(), obj);
359:                        }
360:                    }
361:
362:                } else if (Map.class.isAssignableFrom(attributeType)) {
363:                    Element collectionElement = getCollectionElement(
364:                            attributeName, attributeType);
365:
366:                    collectionElement.removeChildren(attributeName);
367:                    Map map = (Map) newValue;
368:                    for (Iterator mapIterator = map.entrySet().iterator(); mapIterator
369:                            .hasNext();) {
370:
371:                        Map.Entry entry = (Map.Entry) mapIterator.next();
372:                        addAttribute(
373:                                collectionElement,
374:                                attributeName,
375:                                super .getCollectionComponentType(attributeName),
376:                                entry.getValue()).setAttribute(
377:                                MAP_KEY_ATTRIBUTE, (String) entry.getKey());
378:                    }
379:                } else {
380:                    Element attribute = this .element.getChild(attributeName);
381:                    if (attribute != null) {
382:                        // clear the existing value
383:                        this .element.removeContent(attribute);
384:                    }
385:                    if (newValue != null) {
386:                        addAttribute(this .element, attributeName,
387:                                attributeType, newValue);
388:                    }
389:                }
390:            }
391:
392:            /**
393:             * @inherit
394:             * @see AbstractConfigurationProxy#addAttribute
395:             */
396:            public Element addAttribute(String attributeName, Class type,
397:                    Object obj) {
398:                // clear the cached value
399:                this .attributeCache.remove(attributeName);
400:
401:                return addAttribute(getCollectionElement(attributeName,
402:                        Array.class), attributeName, type, obj);
403:            }
404:
405:            /**
406:             * Adds an attribute.
407:             *
408:             * @param element the element to add an attribute to
409:             * @param attributeName the name of the attribute to add
410:             * @param type the classtype of the attribute
411:             * @param obj the attribute to add
412:             * @return the newly created images
413:             */
414:            protected Element addAttribute(Element element,
415:                    String attributeName, Class type, Object obj) {
416:
417:                // create the new attribute
418:                Element newElement = constructElement(attributeName, type, obj);
419:                // add it
420:                element.addContent(newElement);
421:                return newElement;
422:            }
423:
424:            /**
425:             * @inherit
426:             * @see AbstractConfigurationProxy#setArrayValue
427:             */
428:            public void setArrayValue(String attributeName,
429:                    Class attributeType, int index, Object value) {
430:
431:                // clear the cached value
432:                this .attributeCache.remove(attributeName);
433:
434:                Element collectionElement = getCollectionElement(attributeName,
435:                        Array.class);
436:
437:                List children = collectionElement.getChildren(attributeName);
438:
439:                // check for an out of bounds index
440:                if (index < 0 || index >= children.size()) {
441:                    throw new InvalidParameterException(this .getClass(),
442:                            "Index out of bounds: Configuration name ["
443:                                    + this .getConfigurationName()
444:                                    + "], Attribute name [" + attributeName
445:                                    + "], size [" + children.size()
446:                                    + "], requested index [" + index + "]");
447:                }
448:
449:                if (value == null) {
450:                    // the value is null, so remove it from the document
451:                    children.remove(index);
452:
453:                } else {
454:                    // create the new value
455:                    Element newElement = constructElement(attributeName,
456:                            attributeType, value);
457:                    // add it
458:                    children.set(index, newElement);
459:                }
460:            }
461:
462:            /**
463:             * <P>Sets the value of a specific index in an array of data in this
464:             * configuration object. Implementing classes must set the specified index
465:             * of the array named by <code>attributeName</code> to the specified value.
466:             * </P>
467:             *
468:             * @param attributeName the name of the array to alter
469:             * @param attributeType the component type of the value within the map.
470:             * @param key the key within the map to set
471:             * @param value the Object to set as the value of this indicie
472:             */
473:            public void setMapValue(String attributeName, Class attributeType,
474:                    Object key, Object value) {
475:
476:                // clear the cached value
477:                this .attributeCache.remove(attributeName);
478:
479:                Element collectionElement = getCollectionElement(attributeName,
480:                        Map.class);
481:
482:                List children = collectionElement.getChildren(attributeName);
483:
484:                int oldIndex = -1;
485:                // If the value exists, remove it...
486:                // It is not required to exists as this interface has the same general
487:                // symantics as Map.put.
488:                for (int i = 0; i < children.size(); i++) {
489:                    Element contentElement = (Element) children.get(i);
490:                    if (key.equals(contentElement
491:                            .getAttributeValue(MAP_KEY_ATTRIBUTE))) {
492:                        oldIndex = i;
493:                    }
494:                }
495:
496:                if (value == null) {
497:                    // Do not add null values (maps don't support them anyway
498:                } else {
499:                    // create the new value
500:                    Element newElement = constructElement(attributeName,
501:                            attributeType, value);
502:                    newElement.setAttribute(MAP_KEY_ATTRIBUTE, (String) key);
503:                    // add it
504:                    if (oldIndex >= 0) {
505:                        children.set(oldIndex, newElement);
506:                    } else {
507:                        children.add(newElement);
508:                    }
509:                }
510:            }
511:
512:            /**
513:             * Gets the element containing either an array or Map object.
514:             *
515:             * @param attributeName name of the attribute to get the element for
516:             * @param type the type of class contained in the array or Map
517:             * @return element containing the array or Map
518:             */
519:            protected Element getCollectionElement(String attributeName,
520:                    Class type) {
521:                String tagName = attributeName;
522:                if (Map.class.isAssignableFrom(type)) {
523:                    tagName += "Map";
524:                } else {
525:                    tagName += "Array";
526:                }
527:
528:                Element collectionElement = this .element.getChild(tagName);
529:                if (collectionElement == null) {
530:                    collectionElement = new Element(tagName);
531:                    this .element.addContent(collectionElement);
532:                }
533:                return collectionElement;
534:            }
535:
536:            /**
537:             * Converts the element to its object representation.
538:             * <p>
539:             * This implementation will create a new configuration proxy if the
540:             * type is a subtype of Configuration or the type is complex.
541:             * If it is not a configuration subtype, the type handlers
542:             * will be used to convert the data to an object.  If type is
543:             * not a Configuration or complex type and element is null or its type
544:             * handler returns null, this implementation will attempt to lookup a
545:             * default via a call to lookupDefaultAttributeValue from the super class.
546:             * If element is null or its type handler returns null and there is no
547:             * default and type is a primitive, an exception is thrown.
548:             *
549:             * @param element the element to be converted
550:             * @param name the name of the element, used to name the resulting
551:             * object when it is a subtype of Configuration
552:             * @param type expected type of the resulting object
553:             * @return Object the object representation of element
554:             *
555:             * @throws ConfigurationFormatException indicates an error
556:             *         formatting the element
557:             */
558:            protected Object formatElement(Element element, String name,
559:                    Class type) throws ConfigurationFormatException {
560:
561:                try {
562:                    Object formattedElement = null;
563:
564:                    if (Configuration.class.isAssignableFrom(type)) {
565:                        // the data is a child configuration
566:                        if (element != null) {
567:                            formattedElement = getChildConfiguration(type,
568:                                    element, getConfigurationName()
569:                                            + Node.DELIMITER + name);
570:                        }
571:
572:                    } else if (this .typeService.isComplexType(type)) {
573:                        // the data needs to be extracted as a configuration
574:                        // then passed to a type handler
575:                        Configuration subConfiguration = null;
576:
577:                        if (element != null) {
578:                            subConfiguration = getChildConfiguration(
579:                                    this .typeService
580:                                            .getRequiredConfigurationInterface(type),
581:                                    element, getConfigurationName()
582:                                            + Node.DELIMITER + name);
583:                        }
584:
585:                        // the type is complex so convert the sub configuration to
586:                        // an object
587:                        formattedElement = this .typeService.toObject(type,
588:                                subConfiguration);
589:
590:                    } else {
591:                        // the data can be extracted as text
592:                        if (element != null) {
593:                            formattedElement = this .typeService.toObject(type,
594:                                    element.getText());
595:                        }
596:                        // if the attribute is null and it is not a Configuration type,
597:                        // lookup the default
598:                        if (formattedElement == null) {
599:
600:                            formattedElement = lookupDefaultAttributeValue(this 
601:                                    .getConfigurationInterface(), name, type);
602:
603:                            if ((formattedElement == null)
604:                                    && type.isPrimitive()) {
605:                                // Be careful not to return null from methods that
606:                                // return primitives
607:                                throw new InvalidConfigurationException(this 
608:                                        .getClass(), this 
609:                                        .getConfigurationName(), name,
610:                                        "Primitive configurations must have values as "
611:                                                + "they cannot return null.");
612:                            }
613:                        }
614:                    }
615:
616:                    return formattedElement;
617:
618:                } catch (ConfigurationTypeException cte) {
619:                    throw new ConfigurationFormatException(this .getClass(),
620:                            "Could not parse config data in document ["
621:                                    + getConfigurationName()
622:                                    + "], attribute name [" + name
623:                                    + "], expected type [" + type.getName()
624:                                    + "] data was [" + element.getText() + "]",
625:                            cte);
626:                }
627:            }
628:
629:            /**
630:             * Converts the element to its object representation.
631:             * <p>
632:             * The method is the same as formatElement except that it does not
633:             * look up a default for the element if it is null.
634:             *
635:             * @param element the element to be converted
636:             * @param name the name of the element, used to name the resulting
637:             *        object when it is a subtype of Configuration
638:             * @param key the accessor key of the element, used to name the resulting
639:             *        object when it is a subtype of Configuration
640:             * @param type expected type of the resulting object
641:             * @return Object the object representation of element
642:             *
643:             * @throws ConfigurationFormatException indicates an error formatting
644:             *         the contained element
645:             */
646:            protected Object formatContainedElement(Element element,
647:                    String name, String key, Class type)
648:                    throws ConfigurationFormatException {
649:
650:                try {
651:                    Object formattedElement = null;
652:
653:                    if (Configuration.class.isAssignableFrom(type)) {
654:                        // the data is an indexed child configuraiton
655:                        if (element != null) {
656:                            formattedElement = getChildConfiguration(type,
657:                                    element, getConfigurationName()
658:                                            + Node.DELIMITER + name + "[" + key
659:                                            + "]");
660:
661:                        }
662:
663:                    } else if (this .typeService.isComplexType(type)) {
664:                        // the data needs to be extracted as a configuration
665:                        // then passed to a type handler
666:                        Configuration subConfiguration = null;
667:
668:                        if (element != null) {
669:                            subConfiguration = getChildConfiguration(
670:                                    this .typeService
671:                                            .getRequiredConfigurationInterface(type),
672:                                    element, getConfigurationName()
673:                                            + Node.DELIMITER + name + "[" + key
674:                                            + "]");
675:
676:                        }
677:
678:                        formattedElement = this .typeService.toObject(type,
679:                                subConfiguration);
680:
681:                    } else {
682:                        if (element != null) {
683:                            formattedElement = this .typeService.toObject(type,
684:                                    element.getText());
685:                        }
686:                        if ((formattedElement == null) && type.isPrimitive()) {
687:                            // Be careful not to return null from methods that
688:                            // return primitives
689:                            throw new InvalidConfigurationException(this 
690:                                    .getClass(), this .getConfigurationName(),
691:                                    name,
692:                                    "Primitive configurations must have values as "
693:                                            + "they cannot return null.");
694:                        }
695:                    }
696:
697:                    return formattedElement;
698:                } catch (ConfigurationTypeException cte) {
699:                    throw new ConfigurationFormatException(this .getClass(),
700:                            "Could not parse config data in document ["
701:                                    + getConfigurationName()
702:                                    + "], attribute name [" + name
703:                                    + "] index [" + key + "], expected type ["
704:                                    + type.getName() + "] data was ["
705:                                    + element.getText() + "]", cte);
706:                }
707:            }
708:
709:            /**
710:             * <P>Builds a new implementation of the specified configuration class by
711:             * using a JDOMConfigurationProxy as the InvocationHandler for a
712:             * Dynamic Proxy.</P>
713:             * <p>If the configuration is a reference (the element text starts with
714:             * "ref://"), the referenced configuraiton is fetched from the config
715:             * service and returned, otherwise, a new configuration object is created.
716:             * </p>
717:             *
718:             * @param requiredInterface The class that will be implemented by
719:             *        the configuration
720:             * @param element The root element of the configuration object
721:             * @param name the name of the child configuration being retreived
722:             * @return the new configuration object implementation representing the
723:             *         data supplied in the element
724:             */
725:            protected Configuration getChildConfiguration(
726:                    Class requiredInterface, Element element, String name) {
727:
728:                Configuration subConfiguration;
729:
730:                String textValue = element.getTextTrim();
731:                if (textValue.startsWith(REF_PREFIX)) {
732:                    // the data is a reference to another configuration, fetch the
733:                    // configuration
734:                    if (this .isConfigurationWritable()) {
735:                        subConfiguration = Config.getInstance()
736:                                .fetchWritableConfiguration(
737:                                        textValue.substring(REF_PREFIX_LENGTH));
738:                    } else {
739:                        subConfiguration = Config.getInstance()
740:                                .fetchConfiguration(
741:                                        textValue.substring(REF_PREFIX_LENGTH));
742:                    }
743:                } else {
744:
745:                    // not a reference, construct a new proxy
746:                    Class configType = getConfigurationInterface(element,
747:                            requiredInterface);
748:
749:                    JDOMConfigurationProxy invocationHandler = new JDOMConfigurationProxy(
750:                            element, configType);
751:                    invocationHandler.setConfigurationName(name);
752:                    Object proxy = Proxy.newProxyInstance(configType
753:                            .getClassLoader(), new Class[] { configType },
754:                            invocationHandler);
755:
756:                    subConfiguration = (Configuration) proxy;
757:
758:                    if (!isConfigurationWritable()) {
759:                        subConfiguration.setConfigurationReadOnly();
760:                    }
761:                }
762:
763:                // ensure that the actual type is indeed the right type
764:                if (!requiredInterface.isAssignableFrom(subConfiguration
765:                        .getClass())) {
766:
767:                    throw new InvalidConfigurationException(this .getClass(),
768:                            getConfigurationName(), element.getName(),
769:                            "Required interface was not assignable from configured "
770:                                    + "interface, required ["
771:                                    + requiredInterface.getName()
772:                                    + "] configured ["
773:                                    + subConfiguration
774:                                            .getConfigurationInterface() + "]");
775:                }
776:
777:                return subConfiguration;
778:            }
779:
780:            /**
781:             * Constructs a jdom element from the given object by calling the
782:             * ConfigurationTypeService
783:             *
784:             * @param entityName name of the element to construct
785:             * @param type type of data represented by obj
786:             * @param obj data represented by returned element
787:             * @return Element jdom element representation of obj
788:             */
789:            protected Element constructElement(String entityName, Class type,
790:                    Object obj) {
791:
792:                Element newElement;
793:                if (Configuration.class.isAssignableFrom(type)) {
794:                    // It is a sub configuration type, convert it to an element
795:                    newElement = configurationToElement((Configuration) obj);
796:                    newElement.setName(entityName);
797:
798:                } else if (this .typeService.isComplexType(type)) {
799:                    // complex type, convert it to a configuration then to an element
800:                    try {
801:                        Configuration configValue = this .typeService
802:                                .toConfiguration(type, obj);
803:                        newElement = configurationToElement(configValue);
804:                        newElement.setName(entityName);
805:
806:                    } catch (ConfigurationTypeException cte) {
807:                        throw new InvalidConfigurationException(
808:                                this .getClass(), getConfigurationName(),
809:                                entityName,
810:                                "Could not convert object to configuration",
811:                                cte);
812:                    }
813:
814:                } else {
815:                    // simple type, create a simple element and set its text
816:                    newElement = new Element(entityName);
817:                    newElement.setText(this .typeService.toString(type, obj));
818:                }
819:                return newElement;
820:            }
821:
822:            /**
823:             * Converts a configuration to its jdom representation.  If config is a
824:             * nested configuration (either its name is null or its name starts with
825:             * this configuration's name), a clone of its root element is returned.
826:             * If config exists outside of this configuration, a reference to config
827:             * is created.
828:             *
829:             * @param config configuration to be converted
830:             * @return Element jdom represnetation
831:             */
832:            protected Element configurationToElement(Configuration config) {
833:                boolean isNested;
834:
835:                Element configElement;
836:
837:                if (config.getConfigurationName() == null) {
838:                    // nested
839:                    configElement = (Element) config.getRootElement().clone();
840:
841:                } else if (getConfigurationName() == null) {
842:                    // not nested
843:                    configElement = new Element("Configuration");
844:                    configElement.setText(REF_PREFIX
845:                            + config.getConfigurationName());
846:
847:                } else if (config.getConfigurationName().startsWith(
848:                        getConfigurationName() + Node.DELIMITER)) {
849:                    // nested
850:                    configElement = (Element) config.getRootElement().clone();
851:
852:                } else {
853:                    // not nested
854:                    configElement = new Element("Configuration");
855:                    configElement.setText(REF_PREFIX
856:                            + config.getConfigurationName());
857:                }
858:
859:                return configElement;
860:            }
861:
862:            /**
863:             * @inherit
864:             */
865:            public void setConfigurationReadOnly() {
866:                super .setConfigurationReadOnly();
867:                //        // this object should be prepared for multithreaded reads,
868:                //        // the attributeCache needs to by synced to ensure that concurrent
869:                //        // reads to not corrupt the Map
870:                //        this.attributeCache =
871:                //            Collections.synchronizedMap(this.attributeCache);
872:
873:                // commented out until preLoadAttributeCache works
874:                preLoadAttributeCache();
875:            }
876:
877:            /**
878:             * <b>
879:             * This method is not in use as of carbon 1.1. It causes an infinite
880:             * loop in the case of circular references within configs. Until a
881:             * better solution is found for preloading configuration, the
882:             * attributeCache is synchronized when the config is read-only.
883:             * </b>
884:             * <p>
885:             * Loads all the attributes into the attribute cache. This is used when
886:             * the configuration object is switched to read-only mode to prevent
887:             * concurrent reads from corrupting the cache. After this, there should
888:             * never be a lookup that needs to add to the cache; the cache effectively
889:             * becomes read-only.
890:             * <p>
891:             * This implementation uses the Introspector to get all the properties
892:             * of the configuration interface, then looks up each property using either
893:             * lookupAttribute or getArray, depending on whether or not the property
894:             * is indexed.
895:             *
896:             * @since carbon 1.1
897:             */
898:            protected void preLoadAttributeCache() {
899:
900:                try {
901:                    Class configInterface = getConfigurationInterface();
902:
903:                    PropertyDescriptor[] properties = Introspector.getBeanInfo(
904:                            configInterface).getPropertyDescriptors();
905:
906:                    for (int i = 0; i < properties.length; i++) {
907:                        try {
908:                            PropertyDescriptor this Property = properties[i];
909:
910:                            String propertyName = StringUtil
911:                                    .capitalize(this Property.getName());
912:
913:                            Class propertyType = this Property.getPropertyType();
914:
915:                            if (this Property instanceof  IndexedPropertyDescriptor
916:                                    || propertyType.isArray()) {
917:
918:                                getArray(propertyName, propertyType
919:                                        .getComponentType());
920:
921:                            } else if (Map.class.isAssignableFrom(propertyType)) {
922:                                getMap(
923:                                        propertyName,
924:                                        getCollectionComponentType(propertyName));
925:
926:                            } else {
927:                                lookupAttribute(propertyName, propertyType);
928:                            }
929:
930:                            // test logging commented out because it was too verbose (bug 434)
931:                            // TODO: add white box testing to test caching functionality
932:                            //                    if (Logger
933:                            //                        .getLogger()
934:                            //                        .isLogging(source, SeverityEnum.TRACE)) {
935:                            //
936:                            //                        Logger.getLogger().log(
937:                            //                            source,
938:                            //                            SeverityEnum.TRACE,
939:                            //                            "Preloaded attribute ["
940:                            //                                + propertyName
941:                            //                                + "] of type ["
942:                            //                                + thisProperty.getPropertyType()
943:                            //                                + "] with value ["
944:                            //                                + value
945:                            //                                + "]");
946:                            //                    }
947:
948:                        } catch (InvalidConfigurationException ice) {
949:                            // don't care now, cache everything that is cachable
950:                            // getting this exception means the value is somehow
951:                            // invalid and won't ever be cached by this object.
952:                            // If this is really a problem, the next person who asks
953:                            // for this property will get the exception and perhaps
954:                            // be able to give more context.
955:                        }
956:                    }
957:                } catch (IntrospectionException ie) {
958:                    throw new IllegalStateException(
959:                            this .getClass(),
960:                            "Caught IntrospectionException which should not ever happen",
961:                            ie);
962:                }
963:            }
964:
965:        }
ww___w__.ja_v__a___2s__.__co__m_ | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.