Source Code Cross Referenced for TestConfigObjectInvocationHandler.java in  » Net » Terracotta » com » tc » config » schema » 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 » Net » Terracotta » com.tc.config.schema 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /**
002:         * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright notice.  All rights reserved.
003:         */package com.tc.config.schema;
004:
005:        import org.apache.commons.lang.StringUtils;
006:        import org.apache.xmlbeans.XmlObject;
007:        import org.apache.xmlbeans.impl.values.XmlObjectBase;
008:
009:        import com.tc.config.schema.dynamic.BooleanConfigItem;
010:        import com.tc.config.schema.dynamic.ConfigItem;
011:        import com.tc.config.schema.dynamic.ConfigItemListener;
012:        import com.tc.config.schema.dynamic.FileConfigItem;
013:        import com.tc.config.schema.dynamic.IntConfigItem;
014:        import com.tc.config.schema.dynamic.ObjectArrayConfigItem;
015:        import com.tc.config.schema.dynamic.StringArrayConfigItem;
016:        import com.tc.config.schema.dynamic.StringConfigItem;
017:        import com.tc.config.schema.dynamic.XPathBasedConfigItem;
018:        import com.tc.logging.TCLogger;
019:        import com.tc.logging.TCLogging;
020:        import com.tc.util.Assert;
021:
022:        import java.io.File;
023:        import java.lang.reflect.Field;
024:        import java.lang.reflect.InvocationHandler;
025:        import java.lang.reflect.InvocationTargetException;
026:        import java.lang.reflect.Method;
027:        import java.lang.reflect.Modifier;
028:        import java.lang.reflect.Proxy;
029:        import java.util.HashMap;
030:        import java.util.Map;
031:
032:        /**
033:         * This class exists to avoid the massive pain of writing fully-compliant test versions of every single config object in
034:         * the system. This is used by the {@link com.tc.config.schema.setup.TestTVSConfigurationSetupManagerFactory}; see
035:         * there for more details.
036:         * </p>
037:         * <p>
038:         * Basically, you can use this with the {@link java.util.Proxy} stuff and any random config interface (more
039:         * specifically, one that has methods that all take no parameters and return instances of {@link ConfigItem}, or
040:         * subinterfaces thereof), and you'll get back a config object that's special: when you call the normal methods that
041:         * return config items, you can cast the returned item to a {@link com.tc.config.schema.SettableConfigItem}, and that
042:         * will let you set its value. Note that doing anything else with the returned item, like trying to get its value or add
043:         * or remove listeners, will just reward you with a {@link com.tc.util.TCAssertionError}.
044:         * </p>
045:         * <p>
046:         * This class is messy (I am in a ridiculous rush right now to get this code down). The vast majority of its code is
047:         * involved in taking a root XML bean and an XPath, and then descending through child beans &mdash; creating them along
048:         * the way, if necessary &mdash; to
049:         */
050:        public class TestConfigObjectInvocationHandler implements 
051:                InvocationHandler {
052:
053:            private static final TCLogger logger = TCLogging
054:                    .getLogger(TestConfigObjectInvocationHandler.class);
055:
056:            /**
057:             * This is the {@link ConfigItem} implementation we return. Feel free to add interfaces to this list as we create more
058:             * typed subinterfaces of {@link ConfigItem}; the methods shouldn't do anything, just throw
059:             * {@link com.tc.util.TCAssertionError}s.
060:             */
061:            private class OurSettableConfigItem implements  SettableConfigItem,
062:                    BooleanConfigItem, FileConfigItem, IntConfigItem,
063:                    ObjectArrayConfigItem, StringArrayConfigItem,
064:                    StringConfigItem {
065:                private final String xpath;
066:
067:                public OurSettableConfigItem(String xpath) {
068:                    this .xpath = xpath;
069:                }
070:
071:                public synchronized void addListener(
072:                        ConfigItemListener changeListener) {
073:                    throw Assert
074:                            .failure("You can only set values on these items; you shouldn't actually be using them.");
075:                }
076:
077:                public synchronized Object getObject() {
078:                    throw Assert
079:                            .failure("You can only set values on these items; you shouldn't actually be using them.");
080:                }
081:
082:                public synchronized void removeListener(
083:                        ConfigItemListener changeListener) {
084:                    throw Assert
085:                            .failure("You can only set values on these items; you shouldn't actually be using them.");
086:                }
087:
088:                public synchronized void setValue(Object newValue) {
089:                    for (int i = 0; i < beansToSetOn.length; ++i) {
090:                        logger.debug("Setting value " + newValue
091:                                + " on XPath '" + xpath + "' for bean "
092:                                + beansToSetOn[i]);
093:                        setValueOnBean(newValue, beansToSetOn[i], this .xpath);
094:                    }
095:                }
096:
097:                private String toMethodName(String xpathComponent) {
098:                    while (xpathComponent.startsWith("@"))
099:                        xpathComponent = xpathComponent.substring(1);
100:
101:                    StringBuffer out = new StringBuffer();
102:                    char[] in = xpathComponent.toCharArray();
103:                    boolean capitalizeNext = true;
104:
105:                    for (int i = 0; i < in.length; ++i) {
106:                        char ch = in[i];
107:                        if (ch == '-')
108:                            capitalizeNext = true;
109:                        else {
110:                            out.append(capitalizeNext ? Character
111:                                    .toUpperCase(ch) : ch);
112:                            capitalizeNext = false;
113:                        }
114:                    }
115:
116:                    return out.toString();
117:                }
118:
119:                private void setValueOnBean(Object newValue,
120:                        XmlObject beanToSetOn, String theXPath) {
121:                    synchronized (beanToSetOn) {
122:                        if (StringUtils.isBlank(theXPath)) {
123:                            logger.debug("Setting value " + newValue
124:                                    + " directly on bean " + beanToSetOn + ".");
125:                            setValueDirectly(newValue, beanToSetOn);
126:                        } else {
127:                            String remainingComponents = allButFirstComponentOf(theXPath);
128:                            String component = firstComponentOf(theXPath);
129:
130:                            if (StringUtils.isBlank(remainingComponents)) {
131:                                // Special case, look for set...() method or xset...() method that takes an instance of our value type
132:                                if (setValueAsPropertyDirectly(beanToSetOn,
133:                                        component, newValue, "set"))
134:                                    return;
135:                                if (setValueAsPropertyDirectly(beanToSetOn,
136:                                        component, newValue, "xset"))
137:                                    return;
138:                            }
139:
140:                            logger.debug("Looking for property '" + component
141:                                    + "' of bean " + beanToSetOn + ".");
142:                            XmlObject[] children = beanToSetOn
143:                                    .selectPath(component);
144:
145:                            if (children == null || children.length == 0) {
146:                                // Create a new one
147:                                logger.debug("Property '" + component
148:                                        + "' doesn't exist; creating it.");
149:                                XmlObject newChild = createAndAddNewChild(
150:                                        beanToSetOn, component);
151:                                children = new XmlObject[] { newChild };
152:                            }
153:
154:                            for (int i = 0; i < children.length; ++i) {
155:                                logger.debug("Setting value " + newValue
156:                                        + " on child " + (i + 1) + " of "
157:                                        + children.length + ".");
158:                                setValueOnBean(newValue, children[i],
159:                                        remainingComponents);
160:                            }
161:                        }
162:                    }
163:                }
164:
165:                private XmlObject createAndAddNewChild(XmlObject beanToSetOn,
166:                        String name) {
167:                    String asPropertyName = toMethodName(name);
168:                    Class beanClass = beanToSetOn.getClass();
169:                    Method creatorMethod = null;
170:                    String creatorMethodName = "addNew" + asPropertyName;
171:
172:                    try {
173:                        logger
174:                                .debug("Trying to create new child for property '"
175:                                        + name
176:                                        + "' on bean "
177:                                        + beanToSetOn
178:                                        + " of " + beanClass + ".");
179:                        creatorMethod = beanClass.getMethod(creatorMethodName,
180:                                new Class[0]);
181:                        logger.debug("Found creator method " + creatorMethod);
182:                        Assert
183:                                .eval(XmlObject.class
184:                                        .isAssignableFrom(creatorMethod
185:                                                .getReturnType()));
186:                        return (XmlObject) creatorMethod.invoke(beanToSetOn,
187:                                new Object[0]);
188:                    } catch (NoSuchMethodException nsme) {
189:                        // leave it, try setting instead
190:                        logger.debug("Didn't find creator method named '"
191:                                + creatorMethodName + "'");
192:                    } catch (IllegalArgumentException iae) {
193:                        throw Assert.failure("Can't invoke creator method "
194:                                + creatorMethod, iae);
195:                    } catch (IllegalAccessException iae) {
196:                        throw Assert.failure("Can't invoke creator method "
197:                                + creatorMethod, iae);
198:                    } catch (InvocationTargetException ite) {
199:                        throw Assert.failure("Can't invoke creator method "
200:                                + creatorMethod, ite);
201:                    }
202:
203:                    Method[] allMethods = beanClass.getMethods();
204:                    Method xsetMethod = null;
205:                    String methodName = "xset" + asPropertyName;
206:                    String factoryClassName = null;
207:
208:                    try {
209:                        logger.debug("Looking for method named '" + methodName
210:                                + "' instead.");
211:                        for (int i = 0; i < allMethods.length; ++i) {
212:                            Method this Method = allMethods[i];
213:                            if (this Method.getName().equals(methodName)
214:                                    && this Method.getReturnType().equals(
215:                                            Void.TYPE)
216:                                    && this Method.getParameterTypes().length == 1
217:                                    && XmlObject.class
218:                                            .isAssignableFrom(this Method
219:                                                    .getParameterTypes()[0])) {
220:                                if (xsetMethod != null) {
221:                                    // formatting
222:                                    throw Assert.failure("There are multiple '"
223:                                            + methodName + "' methods in "
224:                                            + beanClass + "; one is "
225:                                            + xsetMethod + ", and another is "
226:                                            + this Method + ".");
227:                                }
228:                                xsetMethod = this Method;
229:                            }
230:                        }
231:
232:                        if (xsetMethod == null) {
233:                            // formatting
234:                            throw Assert
235:                                    .failure("There is no method named '"
236:                                            + methodName
237:                                            + "' in "
238:                                            + beanClass
239:                                            + ". If this method does exist, but on a subclass of "
240:                                            + beanClass
241:                                            + ", not on it itself, you may have specified the wrong XPath prefix when you created "
242:                                            + "this invocation handler.");
243:                        }
244:                        logger.debug("Found xset...() method: " + xsetMethod);
245:
246:                        Class newObjectClass = xsetMethod.getParameterTypes()[0];
247:                        factoryClassName = newObjectClass.getName()
248:                                + ".Factory";
249:                        logger.debug("Creating a new instance of "
250:                                + newObjectClass + " using factory class '"
251:                                + factoryClassName + "'.");
252:                        Class factoryClass = Class.forName(factoryClassName);
253:                        Method createMethod = factoryClass.getMethod(
254:                                "newInstance", new Class[0]);
255:                        Assert
256:                                .eval(XmlObject.class
257:                                        .isAssignableFrom(createMethod
258:                                                .getReturnType()));
259:                        Assert.eval(Modifier.isStatic(createMethod
260:                                .getModifiers()));
261:                        XmlObject out = (XmlObject) createMethod.invoke(null,
262:                                null);
263:                        logger.debug("Created new object " + out + ".");
264:                        xsetMethod.invoke(beanToSetOn, new Object[] { out });
265:                        logger.debug("Set new object " + out
266:                                + " via xset...() method " + xsetMethod
267:                                + " on bean " + beanToSetOn);
268:                        return out;
269:                    } catch (ClassNotFoundException cnfe) {
270:                        throw Assert.failure("No factory class '"
271:                                + factoryClassName + "'?", cnfe);
272:                    } catch (IllegalArgumentException iae) {
273:                        throw Assert.failure(
274:                                "Can't create instance using factory "
275:                                        + factoryClassName
276:                                        + " and assign using method "
277:                                        + xsetMethod + "?", iae);
278:                    } catch (IllegalAccessException iae) {
279:                        throw Assert.failure(
280:                                "Can't create instance using factory "
281:                                        + factoryClassName
282:                                        + " and assign using method "
283:                                        + xsetMethod + "?", iae);
284:                    } catch (InvocationTargetException ite) {
285:                        throw Assert.failure(
286:                                "Can't create instance using factory "
287:                                        + factoryClassName
288:                                        + " and assign using method "
289:                                        + xsetMethod + "?", ite);
290:                    } catch (SecurityException se) {
291:                        throw Assert.failure(
292:                                "Can't create instance using factory "
293:                                        + factoryClassName
294:                                        + " and assign using method "
295:                                        + xsetMethod + "?", se);
296:                    } catch (NoSuchMethodException nsme) {
297:                        throw Assert.failure(
298:                                "Can't create instance using factory "
299:                                        + factoryClassName
300:                                        + " and assign using method "
301:                                        + xsetMethod + "?", nsme);
302:                    }
303:                }
304:
305:                private boolean assignmentCompatible(Class parameterType,
306:                        Object actualObject) {
307:                    if (parameterType.isPrimitive()) {
308:                        if (parameterType.equals(Boolean.TYPE))
309:                            return actualObject instanceof  Boolean;
310:                        if (parameterType.equals(Character.TYPE))
311:                            return actualObject instanceof  Character;
312:                        if (parameterType.equals(Byte.TYPE))
313:                            return actualObject instanceof  Byte;
314:                        if (parameterType.equals(Short.TYPE))
315:                            return actualObject instanceof  Short;
316:                        if (parameterType.equals(Integer.TYPE))
317:                            return actualObject instanceof  Integer;
318:                        if (parameterType.equals(Long.TYPE))
319:                            return actualObject instanceof  Long;
320:                        if (parameterType.equals(Float.TYPE))
321:                            return actualObject instanceof  Float;
322:                        if (parameterType.equals(Double.TYPE))
323:                            return actualObject instanceof  Double;
324:                        throw Assert.failure("Unknown primitive type "
325:                                + parameterType);
326:                    } else {
327:                        return actualObject == null
328:                                || parameterType.isInstance(actualObject);
329:                    }
330:                }
331:
332:                private boolean setValueAsPropertyDirectly(XmlObject onBean,
333:                        String propertyName, Object newValue, String prefixToTry) {
334:                    Method[] allMethods = onBean.getClass().getMethods();
335:                    String methodName = prefixToTry
336:                            + toMethodName(propertyName);
337:                    Method setterMethod = null;
338:
339:                    logger.debug("Looking for '" + methodName + "' method on "
340:                            + onBean.getClass() + "...");
341:                    for (int i = 0; i < allMethods.length; ++i) {
342:                        if (allMethods[i].getName().equals(methodName)
343:                                && allMethods[i].getParameterTypes().length == 1) {
344:                            if (setterMethod != null) {
345:                                // formatting
346:                                logger.debug("There are multiple '"
347:                                        + methodName + "' methods in "
348:                                        + onBean.getClass() + "; one is "
349:                                        + setterMethod + ", and another is "
350:                                        + allMethods[i] + ". Returning false.");
351:                                return false;
352:                            }
353:
354:                            setterMethod = allMethods[i];
355:                        }
356:                    }
357:
358:                    if (setterMethod == null) {
359:                        logger.debug("Can't find '" + methodName
360:                                + "' method with one argument on "
361:                                + onBean.getClass() + "; returning false.");
362:                        return false;
363:                    }
364:
365:                    if (newValue == null
366:                            && setterMethod.getParameterTypes()[0]
367:                                    .isPrimitive()) {
368:                        // formatting
369:                        logger
370:                                .debug("Unable to set null as a value on XML bean "
371:                                        + onBean
372:                                        + "; it requires a(n) "
373:                                        + setterMethod.getParameterTypes()[0]
374:                                        + ", which is a primitive type. Returning false.");
375:                        return false;
376:                    }
377:
378:                    if (newValue != null
379:                            && (!assignmentCompatible(setterMethod
380:                                    .getParameterTypes()[0], newValue))) {
381:                        // formatting
382:                        logger.debug("Unable to set value " + newValue
383:                                + " on XML bean " + onBean + " via method "
384:                                + setterMethod + "; it requires an object of "
385:                                + setterMethod.getParameterTypes()[0]
386:                                + ", and this value is of "
387:                                + newValue.getClass()
388:                                + " instead. Returning false.");
389:                        return false;
390:                    }
391:
392:                    try {
393:                        logger.debug("Setting value " + newValue + " on "
394:                                + onBean + " via method " + setterMethod + ".");
395:                        setterMethod.invoke(onBean, new Object[] { newValue });
396:                        return true;
397:                    } catch (IllegalArgumentException iae) {
398:                        throw Assert.failure(
399:                                "Unable to invoke " + setterMethod, iae);
400:                    } catch (IllegalAccessException iae) {
401:                        throw Assert.failure(
402:                                "Unable to invoke " + setterMethod, iae);
403:                    } catch (InvocationTargetException ite) {
404:                        throw Assert.failure(
405:                                "Unable to invoke " + setterMethod, ite);
406:                    }
407:                }
408:
409:                private void setValueDirectly(Object newValue, XmlObject onBean) {
410:                    Method[] allMethods = onBean.getClass().getMethods();
411:                    Method setterMethod = null;
412:                    boolean isArray = newValue != null
413:                            && newValue.getClass().isArray();
414:                    String searchDescrip = isArray ? "set...Array(...[])"
415:                            : "set(...)";
416:
417:                    logger.debug("Looking for " + searchDescrip + " method on "
418:                            + onBean.getClass() + "...");
419:                    for (int i = 0; i < allMethods.length; ++i) {
420:                        if (isArray) {
421:                            if (allMethods[i].getName().startsWith("set")
422:                                    && allMethods[i].getName()
423:                                            .endsWith("Array")
424:                                    && allMethods[i].getParameterTypes().length == 1
425:                                    && allMethods[i].getParameterTypes()[0]
426:                                            .isArray()) {
427:                                if (setterMethod != null) {
428:                                    // formatting
429:                                    throw Assert.failure("There are multiple '"
430:                                            + searchDescrip + "' methods in "
431:                                            + onBean.getClass() + "; one is "
432:                                            + setterMethod
433:                                            + ", and another is "
434:                                            + allMethods[i]);
435:                                }
436:
437:                                setterMethod = allMethods[i];
438:                            }
439:                        } else {
440:                            Class declaringClass = allMethods[i]
441:                                    .getDeclaringClass();
442:                            if (declaringClass.equals(XmlObject.class)
443:                                    || declaringClass
444:                                            .equals(XmlObjectBase.class))
445:                                continue;
446:
447:                            if (allMethods[i].getName().equals("set")
448:                                    && allMethods[i].getParameterTypes().length == 1) {
449:                                if (setterMethod != null) {
450:                                    // formatting
451:                                    throw Assert.failure("There are multiple '"
452:                                            + searchDescrip + "' methods in "
453:                                            + onBean.getClass() + "; one is "
454:                                            + setterMethod
455:                                            + ", and another is "
456:                                            + allMethods[i]);
457:                                }
458:
459:                                setterMethod = allMethods[i];
460:                            }
461:                        }
462:                    }
463:
464:                    if (setterMethod == null)
465:                        throw Assert.failure("Can't find '" + searchDescrip
466:                                + "' method with one argument on "
467:                                + onBean.getClass() + ".");
468:
469:                    if (newValue == null
470:                            && setterMethod.getParameterTypes()[0]
471:                                    .isPrimitive()) {
472:                        // formatting
473:                        throw Assert
474:                                .failure("You can't set null as a value on XML bean "
475:                                        + onBean
476:                                        + "; it requires a(n) "
477:                                        + setterMethod.getParameterTypes()[0]
478:                                        + ", which is a primitive type.");
479:                    }
480:
481:                    if (newValue != null
482:                            && (!setterMethod.getParameterTypes()[0]
483:                                    .isInstance(newValue))) {
484:                        // formatting
485:                        throw Assert.failure("You can't set value " + newValue
486:                                + " on XML bean " + onBean + " via method "
487:                                + setterMethod + "; it requires an object of "
488:                                + setterMethod.getParameterTypes()[0]
489:                                + ", and your value is of "
490:                                + newValue.getClass() + " instead.");
491:                    }
492:
493:                    try {
494:                        logger.debug("Setting value " + newValue + " on "
495:                                + onBean + " via method " + setterMethod + ".");
496:                        setterMethod.invoke(onBean, new Object[] { newValue });
497:                    } catch (IllegalArgumentException iae) {
498:                        throw Assert.failure(
499:                                "Unable to invoke " + setterMethod, iae);
500:                    } catch (IllegalAccessException iae) {
501:                        throw Assert.failure(
502:                                "Unable to invoke " + setterMethod, iae);
503:                    } catch (InvocationTargetException ite) {
504:                        throw Assert.failure(
505:                                "Unable to invoke " + setterMethod, ite);
506:                    }
507:                }
508:
509:                private String firstComponentOf(String theXPath) {
510:                    while (theXPath.startsWith("/"))
511:                        theXPath = theXPath.substring(1);
512:                    int index = theXPath.indexOf("/");
513:                    if (index < 0)
514:                        return theXPath;
515:                    Assert.eval(index > 0);
516:                    String out = theXPath.substring(0, index);
517:                    while (out.startsWith("@"))
518:                        out = out.substring(1);
519:                    return out;
520:                }
521:
522:                private String allButFirstComponentOf(String theXPath) {
523:                    while (theXPath.startsWith("/"))
524:                        theXPath = theXPath.substring(1);
525:                    int index = theXPath.indexOf("/");
526:                    if (index < 0)
527:                        return null;
528:                    Assert.eval(index > 0);
529:                    return theXPath.substring(index + 1);
530:                }
531:
532:                public void setValue(boolean newValue) {
533:                    setValue(new Boolean(newValue));
534:                }
535:
536:                public void setValue(int newValue) {
537:                    setValue(new Integer(newValue));
538:                }
539:
540:                public boolean getBoolean() {
541:                    return ((Boolean) getObject()).booleanValue();
542:                }
543:
544:                public String getString() {
545:                    return (String) getObject();
546:                }
547:
548:                public String[] getStringArray() {
549:                    return (String[]) getObject();
550:                }
551:
552:                public File getFile() {
553:                    return (File) getObject();
554:                }
555:
556:                public int getInt() {
557:                    return ((Integer) getObject()).intValue();
558:                }
559:
560:                public Object[] getObjects() {
561:                    return (Object[]) getObject();
562:                }
563:            }
564:
565:            private final Class theInterface;
566:            private final XmlObject[] beansToSetOn;
567:            private final Object realImplementation;
568:            private final String xpathPrefix;
569:
570:            private final Map configItems;
571:
572:            // The XPath prefix is because when we grab XPaths out of XPathBasedConfigItems, they're relative to the bean that
573:            // that config object is based on, which might be a child of the one in the repository (using the ChildBeanRepository
574:            // stuff). This path needs to be the XPath from the root of the repository to the bean used as the root of the config
575:            // object.
576:            public TestConfigObjectInvocationHandler(Class theInterface,
577:                    XmlObject[] beansToSetOn, Object realImplementation,
578:                    String xpathPrefix) {
579:                Assert.assertNotNull(theInterface);
580:                Assert.assertNoNullElements(beansToSetOn);
581:                Assert.assertNotNull(realImplementation);
582:
583:                Assert.eval(theInterface.isInstance(realImplementation));
584:
585:                this .theInterface = theInterface;
586:                this .beansToSetOn = beansToSetOn;
587:                this .realImplementation = realImplementation;
588:                this .xpathPrefix = xpathPrefix;
589:
590:                this .configItems = new HashMap();
591:            }
592:
593:            public Object invoke(Object proxy, Method method, Object[] args)
594:                    throws Throwable {
595:                Assert.assertNotNull(proxy);
596:                Assert.assertNotNull(method);
597:
598:                // It's a getter method
599:                if (ConfigItem.class.isAssignableFrom(method.getReturnType())) {
600:                    String propertyName = method.getName();
601:                    return itemForProperty(propertyName, method);
602:                } else if (NewConfig.class.isAssignableFrom(method
603:                        .getReturnType())) {
604:                    Object newRealObject = method.invoke(
605:                            this .realImplementation, new Object[0]);
606:                    String subXPath = fetchSubXPathFor(method.getName());
607:                    String xpath;
608:                    if (xpathPrefix != null && subXPath != null)
609:                        xpath = xpathPrefix + "/" + subXPath;
610:                    else if (xpathPrefix != null)
611:                        xpath = xpathPrefix;
612:                    else
613:                        xpath = subXPath;
614:                    System.err.println("Creating new sub-config object of "
615:                            + method.getReturnType());
616:                    System.err.println("XPath: " + xpath);
617:                    System.err.println("New real object: " + newRealObject);
618:                    return Proxy.newProxyInstance(getClass().getClassLoader(),
619:                            new Class[] { method.getReturnType() },
620:                            new TestConfigObjectInvocationHandler(method
621:                                    .getReturnType(), this .beansToSetOn,
622:                                    newRealObject, xpath));
623:                } else {
624:                    throw Assert
625:                            .failure("This method, '"
626:                                    + method.getName()
627:                                    + "', has a return type of "
628:                                    + method.getReturnType().getName()
629:                                    + ", which isn't a descendant of ConfigItem. "
630:                                    + "We don't know how to support this for test config objects yet.");
631:                }
632:            }
633:
634:            private String fetchSubXPathFor(String methodName) {
635:                String fieldName = camelToUnderscores(methodName)
636:                        + "_SUB_XPATH";
637:
638:                try {
639:                    Field theField = this .realImplementation.getClass()
640:                            .getField(fieldName);
641:                    Assert.eval(Modifier.isPublic(theField.getModifiers()));
642:                    Assert.eval(Modifier.isStatic(theField.getModifiers()));
643:                    Assert.eval(Modifier.isFinal(theField.getModifiers()));
644:                    Assert.eval(String.class.equals(theField.getType()));
645:                    return (String) theField.get(null);
646:                } catch (IllegalAccessException iae) {
647:                    throw Assert.failure("Can't fetch field '" + fieldName
648:                            + "'?", iae);
649:                } catch (NoSuchFieldException nsfe) {
650:                    return null;
651:                }
652:            }
653:
654:            private String camelToUnderscores(String methodName) {
655:                StringBuffer out = new StringBuffer();
656:                char[] source = methodName.toCharArray();
657:                boolean lastWasLowercase = isLowercase(source[0]);
658:                out.append(toUppercase(source[0]));
659:
660:                for (int i = 1; i < source.length; ++i) {
661:                    boolean this IsLowercase = isLowercase(source[i]);
662:
663:                    if (lastWasLowercase && (!this IsLowercase)) {
664:                        // transition
665:                        out.append("_");
666:                    }
667:
668:                    lastWasLowercase = this IsLowercase;
669:                    out.append(toUppercase(source[i]));
670:                }
671:
672:                return out.toString();
673:            }
674:
675:            private boolean isLowercase(char ch) {
676:                return Character.isLowerCase(ch);
677:            }
678:
679:            private char toUppercase(char ch) {
680:                return Character.toUpperCase(ch);
681:            }
682:
683:            private synchronized OurSettableConfigItem itemForProperty(
684:                    String propertyName, Method theMethod) {
685:                OurSettableConfigItem out = (OurSettableConfigItem) this .configItems
686:                        .get(propertyName);
687:                if (out == null) {
688:                    try {
689:                        ConfigItem realItem = (ConfigItem) theMethod.invoke(
690:                                realImplementation, new Object[0]);
691:                        Assert.eval("The base item we found was a "
692:                                + realItem.getClass()
693:                                + ", not an XPathBasedConfigItem",
694:                                realItem instanceof  XPathBasedConfigItem);
695:
696:                        String effectiveXPath = (this .xpathPrefix == null ? ""
697:                                : this .xpathPrefix + "/")
698:                                + ((XPathBasedConfigItem) realItem).xpath();
699:                        logger
700:                                .debug("For property '"
701:                                        + propertyName
702:                                        + "' on proxied config-object implementation of interface "
703:                                        + this .theInterface.getName()
704:                                        + ", returning a config item with effective XPath '"
705:                                        + effectiveXPath + "'.");
706:
707:                        out = new OurSettableConfigItem(effectiveXPath);
708:                        this .configItems.put(propertyName, out);
709:                    } catch (IllegalAccessException iae) {
710:                        throw Assert
711:                                .failure(
712:                                        "Unable to retrieve the real ConfigItem so we can get its XPath.",
713:                                        iae);
714:                    } catch (InvocationTargetException ite) {
715:                        throw Assert
716:                                .failure(
717:                                        "Unable to retrieve the real ConfigItem so we can get its XPath.",
718:                                        ite);
719:                    }
720:                }
721:                return out;
722:            }
723:
724:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.