r1017 - in lutinjaxx/trunk: jaxx-core jaxx-core/src/main/java/jaxx/compiler jaxx-core/src/main/java/jaxx/runtime jaxx-core/src/main/java/jaxx/runtime/swing jaxx-core/src/main/java/jaxx/runtime/validator jaxx-core/src/main/java/jaxx/tags jaxx-core/src/main/java/jaxx/tags/validator jaxx-core/src/site/fr/rst jaxx-core/src/test/java/jaxx/runtime jaxx-core/src/test/java/jaxx/runtime/swing maven-jaxx-plugin maven-jaxx-plugin/src/main/java/org/codelutin/jaxx
Author: tchemit Date: 2008-11-16 17:44:15 +0000 (Sun, 16 Nov 2008) New Revision: 1017 Added: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/JAXXObjectGeneratorConstants.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/Decorator.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/swing/CardLayout2.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/swing/NavigationTreeModel.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/swing/NavigationTreeSelectionAdapter.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/swing/NavigationtreeSelectionAdapterWithCardLayout.java lutinjaxx/trunk/jaxx-core/src/site/fr/rst/BeanValidator.rst lutinjaxx/trunk/jaxx-core/src/site/fr/rst/I18n.rst lutinjaxx/trunk/jaxx-core/src/site/fr/rst/Interface.rst lutinjaxx/trunk/jaxx-core/src/site/fr/rst/JAXXContext.rst lutinjaxx/trunk/jaxx-core/src/site/fr/rst/JavaBean.rst lutinjaxx/trunk/jaxx-core/src/site/fr/rst/NavigationTreeModel.rst lutinjaxx/trunk/jaxx-core/src/test/java/jaxx/runtime/swing/ lutinjaxx/trunk/jaxx-core/src/test/java/jaxx/runtime/swing/NavigationTreeModelTest.java Modified: lutinjaxx/trunk/jaxx-core/changelog lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/CompiledObject.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/CompilerOptions.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/DataBinding.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/DataSource.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/JAXXCompiler.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/JAXXObjectGenerator.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/JavaField.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/JavaFile.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/JavaMethod.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/DefaultJAXXContext.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/JAXXContext.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/JAXXContextEntryDef.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/JAXXInitialContext.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/JAXXObject.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/Util.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/swing/Utils.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/validator/BeanValidator.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/validator/ErrorListMouseListener.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/DefaultObjectHandler.java lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/validator/BeanValidatorHandler.java lutinjaxx/trunk/jaxx-core/src/site/fr/rst/Todo.rst lutinjaxx/trunk/jaxx-core/src/site/fr/rst/index.rst lutinjaxx/trunk/jaxx-core/src/test/java/jaxx/runtime/DefaultJAXXContextTest.java lutinjaxx/trunk/maven-jaxx-plugin/changelog lutinjaxx/trunk/maven-jaxx-plugin/src/main/java/org/codelutin/jaxx/JaxxGeneratorMojo.java Log: * 20081107 [chemit] improve data binding and code generation : - make possible inheritance in binding - add an attribute javaBean to an object : will generate a full java bean support property - make possible binding to the javaBean added properties - clean generated code * 20081105 [chemit] introduce a CardLayout2 to extends awt CardLayout introduce a NavigationTreeModel introduce a Decorator to render Object propagate constructor JAXXContext(JAXXContext) in JAXXObject generation begin of rst documentation Modified: lutinjaxx/trunk/jaxx-core/changelog =================================================================== --- lutinjaxx/trunk/jaxx-core/changelog 2008-11-16 17:42:00 UTC (rev 1016) +++ lutinjaxx/trunk/jaxx-core/changelog 2008-11-16 17:44:15 UTC (rev 1017) @@ -1,4 +1,16 @@ 0.6 chemit 200811?? + * 20081107 [chemit] improve data binding and code generation : + - make possible inheritance in binding + - add an attribute javaBean to an object : will generate a full java bean support property + - make possible binding to the javaBean added properties + - clean generated code + + * 20081105 [chemit] introduce a CardLayout2 to extends awt CardLayout + introduce a NavigationTreeModel + introduce a Decorator to render Object + propagate constructor JAXXContext(JAXXContext) in JAXXObject generation + begin of rst documentation + * 20081104 [chemit] can add extra beanInfoSearchPath in SwingInitializer * 20081104 [chemit] add jaxxContextImplementorClass in option to make possible use of other JAXXContext implementor. * 20081102 [chemit] improve JAXXContext : Modified: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/CompiledObject.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/CompiledObject.java 2008-11-16 17:42:00 UTC (rev 1016) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/CompiledObject.java 2008-11-16 17:44:15 UTC (rev 1017) @@ -74,6 +74,12 @@ /** generic types of the compiled object */ private String[] genericTypes; + /** a flag to indicate if javaBean full support must be support for this object by root object */ + private boolean javaBean; + + /** the type of the override object (can be null if no oveeride) */ + private ClassDescriptor overrideType; + /** * Creates a new <code>CompiledObject</code>. To be useful, the object should be registered with a * <code>JAXXCompiler</code> using {@link JAXXCompiler#registerCompiledObject registerCompiledObject}. @@ -278,7 +284,27 @@ return result; } + public String getJavaCodeForProperty(String property) { + if (!override) { + return javaCode; + } + String result = "((" + JAXXCompiler.getCanonicalName(getObjectClass()) + ") " + javaCode + ")"; + String methodName = JAXXCompiler.capitalize(property); + try { + MethodDescriptor methodDescriptor = overrideType.getMethodDescriptor("get" + methodName, new ClassDescriptor[0]); + if (methodDescriptor != null) { + if (overrideType.getMethodDescriptor("set" + methodName, new ClassDescriptor[]{methodDescriptor.getReturnType()}) != null) { + // handle cases where object is overridden to be a different class + result = javaCode; + } + } + } catch (NoSuchMethodException e) { + // lazy error, do nothing + } + return result; + } + /** * Returns a list of comma-separated Java code snippets that represent the parameters to pass to this * object's constructor. @@ -480,9 +506,11 @@ } if (constraints != null) { - appendAdditionCode(getJavaCode() + delegateCode + ".add(" + javaCode + ", " + constraints + ");"); + appendAdditionCode(this.javaCode + delegateCode + ".add(" + javaCode + ", " + constraints + ");"); + //appendAdditionCode(getJavaCode() + delegateCode + ".add(" + javaCode + ", " + constraints + ");"); } else { - appendAdditionCode(getJavaCode() + delegateCode + ".add(" + javaCode + ");"); + appendAdditionCode(this.javaCode + delegateCode + ".add(" + javaCode + ");"); + //appendAdditionCode(getJavaCode() + delegateCode + ".add(" + javaCode + ");"); } } } @@ -517,4 +545,20 @@ public void setGenericTypes(String[] genericTypes) { this.genericTypes = genericTypes; } + + public boolean isJavaBean() { + return javaBean; + } + + public void setJavaBean(boolean javaBean) { + this.javaBean = javaBean; + } + + public ClassDescriptor getOverrideType() { + return overrideType; + } + + public void setOverrideType(ClassDescriptor overrideType) { + this.overrideType = overrideType; + } } \ No newline at end of file Modified: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/CompilerOptions.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/CompilerOptions.java 2008-11-16 17:42:00 UTC (rev 1016) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/CompilerOptions.java 2008-11-16 17:44:15 UTC (rev 1017) @@ -23,6 +23,8 @@ /** the name of implementation of {@link jaxx.runtime.JAXXContext} to be used on {@link jaxx.runtime.JAXXObject}. */ protected String jaxxContextImplementorClass; + /** list of fqn of class toimport for all generated jaxx files */ + protected String[] extraImports; /** * Returns the target directory, generally specified with the "-d" option on the command line. * @@ -181,6 +183,14 @@ this.jaxxContextImplementorClass = jaxxContextImplementorClass; } + public String[] getExtraImports() { + return extraImports; + } + + public void setExtraImports(String[] extraImports) { + this.extraImports = extraImports; + } + @Override public String toString() { StringBuilder sb = new StringBuilder(super.toString()); @@ -193,6 +203,9 @@ sb.append("\ni18nable : ").append(isI18nable()); sb.append("\naddLogger : ").append(isAddLogger()); sb.append("\njaxxContextImplementorClass : ").append(getJaxxContextImplementorClass()); + sb.append("\nextraImports : ").append(getExtraImports()); return sb.toString(); } + + } Modified: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/DataBinding.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/DataBinding.java 2008-11-16 17:42:00 UTC (rev 1016) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/DataBinding.java 2008-11-16 17:44:15 UTC (rev 1017) @@ -66,13 +66,17 @@ if (!result && quickNoDependencies) { if (!dest.endsWith(".layout")) // layout is specially handled early in the chain - compiler.appendInitDataBindings(assignment+ JAXXCompiler.getLineSeparator()); + { + compiler.appendInitDataBindings(assignment + JAXXCompiler.getLineSeparator()); + } return false; // no dependencies, just a static expression } if (compiler.haveProcessDataBinding()) { - compiler.appendProcessDataBinding("else "); + compiler.appendProcessDataBinding(" else "); + } else { + compiler.appendProcessDataBinding(" "); } - compiler.appendProcessDataBinding("if ($dest.equals(" + TypeManager.getJavaCode(id) + ")) { " + assignment + "}\n" + JAXXCompiler.getLineSeparator()); + compiler.appendProcessDataBinding("if (" + TypeManager.getJavaCode(id) + ".equals($dest)) {" + JAXXCompiler.getLineSeparator() +" "+ assignment + JAXXCompiler.getLineSeparator() + " }"); return true; } } \ No newline at end of file Modified: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/DataSource.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/DataSource.java 2008-11-16 17:42:00 UTC (rev 1016) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/DataSource.java 2008-11-16 17:44:15 UTC (rev 1017) @@ -19,7 +19,9 @@ import jaxx.types.TypeManager; import java.beans.Introspector; +import java.beans.PropertyChangeListener; import java.io.StringReader; +import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -70,6 +72,10 @@ return id; } + public String getSource() { + return source; + } + /** * Compiles the data source expression and listener. This method calls methods in <code>JAXXCompiler</code> * to add the Java code that performs the data source setup. Adding listeners to <code>DataSource</code> is @@ -93,7 +99,9 @@ } if (dependencySymbols.size() > 0) { - compiler.appendBodyCode("private PropertyChangeListener " + id + " = " + propertyChangeListenerCode + ";\n"); + //TC 20081108 prefer add a real JavaField instead of raw code + //compiler.appendBodyCode("private PropertyChangeListener " + id + " = " + propertyChangeListenerCode + ";\n"); + compiler.addSimpleField(new JavaField(Modifier.PRIVATE, PropertyChangeListener.class.getName(), id, propertyChangeListenerCode)); } compileListeners(); @@ -167,16 +175,16 @@ * Scans through a compound symbol (foo.bar.baz) to identify and track all trackable pieces of it. * * @param symbol symbol to scan - * @param contextClass - * @param isMethod - * @param listenerId + * @param contextClass current class context + * @param isMethod flag to search a method + * @param listenerId id of the listener * @return the type of the symbol (or null if it could not be determined). */ private ClassDescriptor scanCompoundSymbol(String symbol, ClassDescriptor contextClass, boolean isMethod, String listenerId) { String[] tokens = symbol.split("\\s*\\.\\s*"); StringBuffer currentSymbol = new StringBuffer(); StringBuffer tokensSeenSoFar = new StringBuffer(); - boolean accepted = true; // if this ends up false, it means we weren't able to figure out + boolean accepted; // if this ends up false, it means we weren't able to figure out // which object the method is being invoked on boolean recognizeClassNames = true; for (int j = 0; j < tokens.length - (isMethod ? 1 : 0); j++) { @@ -251,9 +259,9 @@ * Adds type information to nodes where possible, and as a side effect adds event listeners to nodes which * can be tracked. * - * @param expression - * @param listenerId - * @return + * @param expression the node to scan + * @param listenerId id of the listener + * @return the class descriptor of the return type or null */ private ClassDescriptor determineExpressionType(SimpleNode expression, String listenerId) { assert expression.getId() == JavaParserTreeConstants.JJTPRIMARYEXPRESSION; @@ -391,30 +399,31 @@ addListenerCode.append("if (").append(objectCode).append(" != null) {").append(JAXXCompiler.getLineSeparator()); addListenerCode.append(" "); } - addListenerCode.append(addCode); + addListenerCode.append(" ").append(addCode); if (objectCode != null) { - addListenerCode.append("}"); + addListenerCode.append(" ").append("}"); } if (objectCode != null) { removeListenerCode.append("if (").append(objectCode).append(" != null) {").append(JAXXCompiler.getLineSeparator()); removeListenerCode.append(" "); } - removeListenerCode.append(removeCode); + removeListenerCode.append(" ").append(removeCode); if (objectCode != null) { - removeListenerCode.append("}"); + removeListenerCode.append(" }"); } } } private void compileListeners() { + String javaCodeId = TypeManager.getJavaCode(id); if (addListenerCode.length() > 0) { if (compiler.haveApplyDataBinding()) { - compiler.appendApplyDataBinding("else "); + compiler.appendApplyDataBinding(" else "); } - compiler.appendApplyDataBinding("if ($binding.equals(" + TypeManager.getJavaCode(id) + ")) {" + JAXXCompiler.getLineSeparator()); + compiler.appendApplyDataBinding("if (" + javaCodeId + ".equals($binding)) {" + JAXXCompiler.getLineSeparator()); compiler.appendApplyDataBinding(" " + addListenerCode + JAXXCompiler.getLineSeparator()); - compiler.appendApplyDataBinding("}" + JAXXCompiler.getLineSeparator()); + compiler.appendApplyDataBinding("}"); //if (compiler.applyDataBinding.length() > 0) // compiler.applyDataBinding.append("else "); //compiler.applyDataBinding.append("if ($binding.equals(").append(TypeManager.getJavaCode(id)).append(")) {").append(JAXXCompiler.getLineSeparator()); @@ -424,11 +433,11 @@ if (removeListenerCode.length() > 0) { if (compiler.haveRemoveDataBinding()) { - compiler.appendRemoveDataBinding("else "); + compiler.appendRemoveDataBinding(" else "); } - compiler.appendRemoveDataBinding("if ($binding.equals(" + TypeManager.getJavaCode(id) + ")) {" + JAXXCompiler.getLineSeparator()); + compiler.appendRemoveDataBinding("if (" + javaCodeId + ".equals($binding)) {" + JAXXCompiler.getLineSeparator()); compiler.appendRemoveDataBinding(" " + removeListenerCode + JAXXCompiler.getLineSeparator()); - compiler.appendRemoveDataBinding("}" + JAXXCompiler.getLineSeparator()); + compiler.appendRemoveDataBinding("}"); //if (compiler.removeDataBinding.length() > 0) // compiler.removeDataBinding.append("else "); //compiler.removeDataBinding.append("if ($binding.equals(").append(TypeManager.getJavaCode(id)).append(")) {").append(JAXXCompiler.getLineSeparator()); Modified: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/JAXXCompiler.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/JAXXCompiler.java 2008-11-16 17:42:00 UTC (rev 1016) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/JAXXCompiler.java 2008-11-16 17:44:15 UTC (rev 1017) @@ -29,6 +29,7 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.lang.reflect.Modifier; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; @@ -139,6 +140,9 @@ /** right brace matcher */ protected Matcher rightBraceMatcher = Pattern.compile("^(\\})|[^\\\\](\\})").matcher(""); + /** extra interfaces which can by passed to root object via the 'implements' attribute */ + private String[] extraInterfaces; + /*---------------------------------------------------------------------------------*/ /*-- Constructor methods ----------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/ @@ -169,6 +173,12 @@ for (Object staticImport : staticImports) { addImport((String) staticImport); } + // add extra imports from options + if (options.getExtraImports() != null) { + for (String extraImport : options.getExtraImports()) { + addImport(extraImport); + } + } } /*---------------------------------------------------------------------------------*/ @@ -239,6 +249,16 @@ String id = tag.getAttribute("id"); if (id.length() > 0) { symbolTable.getClassTagIds().put(id, fullClassName); + if (tag.getAttributeNode("javaBean") != null) { + // add java bean support for this property + String capitalizeName = capitalize(id); + // add method + symbolTable.getScriptMethods().add(new MethodDescriptor("get" + capitalizeName, Modifier.PUBLIC, fullClassName, new String[0], classLoader)); + if (Boolean.class.getName().equals(fullClassName)) { + symbolTable.getScriptMethods().add(new MethodDescriptor("is" + capitalizeName, Modifier.PUBLIC, fullClassName, new String[0], classLoader)); + } + symbolTable.getScriptMethods().add(new MethodDescriptor("set" + capitalizeName, Modifier.PUBLIC, "void", new String[]{fullClassName}, classLoader)); + } } } // during the first pass, we can't create ClassDescriptors for JAXX files because they may not have been processed yet @@ -403,6 +423,7 @@ reportError("attempting to redefine superclass member '" + object.getId() + "' as incompatible type (was " + f.getType() + ", redefined as " + object.getObjectClass() + ")"); } object.setOverride(true); + object.setOverrideType(f.getType()); break; } catch (NoSuchFieldException e) { @@ -1142,4 +1163,15 @@ return result; } + public void setExtraInterfaces(String[] extraInterfaces) { + this.extraInterfaces = extraInterfaces; + } + + public String[] getExtraInterfaces() { + return extraInterfaces; + } + + public void addSimpleField(JavaField javaField) { + generator.getJavaFile().addSimpleField(javaField); + } } Modified: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/JAXXObjectGenerator.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/JAXXObjectGenerator.java 2008-11-16 17:42:00 UTC (rev 1016) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/JAXXObjectGenerator.java 2008-11-16 17:44:15 UTC (rev 1017) @@ -7,6 +7,7 @@ import jaxx.runtime.JAXXObject; import jaxx.runtime.JAXXObjectDescriptor; import jaxx.runtime.swing.Application; +import jaxx.runtime.validator.BeanValidator; import jaxx.tags.validator.BeanValidatorHandler.CompiledBeanValidator; import jaxx.types.TypeManager; import org.apache.commons.logging.Log; @@ -32,7 +33,7 @@ * * @author chemit */ -public class JAXXObjectGenerator { +public class JAXXObjectGenerator extends JAXXObjectGeneratorConstants { /** log */ protected static final Log log = LogFactory.getLog(JAXXObjectGenerator.class); @@ -70,6 +71,7 @@ /** true if a main() method has been declared in a script */ protected boolean mainDeclared; + public JAXXObjectGenerator(JAXXCompiler compiler) { this.compiler = compiler; } @@ -106,53 +108,92 @@ if (root == null) { throw new CompilerException("root tag must be a class tag"); } - Map<String, CompiledObject> objects = compiler.getObjects(); + //Map<String, CompiledObject> objects = compiler.getObjects(); ClassDescriptor superclass = root.getObjectClass(); boolean superclassIsJAXXObject = ClassDescriptorLoader.getClassDescriptor(JAXXObject.class).isAssignableFrom(superclass); javaFile.setModifiers(Modifier.PUBLIC); javaFile.setClassName(fullClassName); javaFile.setSuperClass(JAXXCompiler.getCanonicalName(superclass)); - javaFile.setInterfaces(new String[]{JAXXCompiler.getCanonicalName(JAXXObject.class)}); + // can have extra interfaces from compiler + java.util.Set<String> interfaces = new java.util.HashSet<String>(); + interfaces.add(JAXXCompiler.getCanonicalName(JAXXObject.class)); + if (compiler.getExtraInterfaces() != null) { + interfaces.addAll(java.util.Arrays.asList(compiler.getExtraInterfaces())); + } + javaFile.setInterfaces(interfaces.toArray(new String[interfaces.size()])); for (CompiledObject object : compiler.getObjects().values()) { if (!object.isOverride() && !(object instanceof ScriptInitializer)) { - int access = object.getId().startsWith("$") ? Modifier.PRIVATE : Modifier.PROTECTED; + String id = object.getId(); + int access = id.startsWith("$") ? Modifier.PRIVATE : Modifier.PROTECTED; if (object == root) { - javaFile.addField(new JavaField(access, fullClassName, object.getId(), "this")); + javaFile.addField(new JavaField(access, fullClassName, id, "this")); } else { //TC -20081017 can have generic on compiled Object - javaFile.addField(new JavaField(access, JAXXCompiler.getCanonicalName(object), object.getId())); - //javaFile.addField(new JavaField(access, getCanonicalName(object.getObjectClass()), object.getId())); + javaFile.addField(newField(access, JAXXCompiler.getCanonicalName(object), id), object.isJavaBean()); } } + + if (!compiler.inlineCreation(object) && object != root) { + javaFile.addMethod(newMethod(Modifier.PROTECTED, "void", object.getCreationMethodName(), getCreationCode(object))); + } } + String jaxxContextImplementorClass = compiler.getOptions().getJaxxContextImplementorClass(); + if (!superclassIsJAXXObject) { // add logger if (getOptions().isAddLogger()) { - javaFile.addImport("org.apache.commons.logging.Log"); - javaFile.addImport("org.apache.commons.logging.LogFactory"); - javaFile.addField(createLoggerField(fullClassName)); + javaFile.addImport(org.apache.commons.logging.Log.class); + javaFile.addImport(org.apache.commons.logging.LogFactory.class); + javaFile.addField(newField(Modifier.PUBLIC + Modifier.STATIC + Modifier.FINAL, "Log", "log", "LogFactory.getLog(" + fullClassName + ".class)")); } - javaFile.addField(new JavaField(Modifier.PROTECTED, "java.util.List<Object>", "$activeBindings", "new ArrayList<Object>()")); - javaFile.addField(new JavaField(Modifier.PROTECTED, "java.util.Map<String,Object>", "$bindingSources", "new HashMap<String,Object>()")); - javaFile.addField(new JavaField(Modifier.PROTECTED, "java.util.List<String>", "validatorIds", "new ArrayList<String>()")); + // JAXXObject + javaFile.addField(OBJECT_MAP_FIELD); + javaFile.addMethod(GET_OBJECT_BY_ID_METHOD); + javaFile.addField(BINDING_SOURCES_FIELD); + javaFile.addField(ACTIVE_BINDINGS_FIELD); + + // JAXXContext + + javaFile.addField(DELEGATE_CONTEXT_FIELD); + javaFile.addMethod(SET_CONTEXT_VALUE_METHOD); + javaFile.addMethod(SET_CONTEXT_VALUE_NAMED_METHOD); + javaFile.addMethod(GET_CONTEXT_VALUE_METHOD); + javaFile.addMethod(GET_CONTEXT_VALUE_NAMED_METHOD); + javaFile.addMethod(REMOVE_CONTEXT_VALUE_METHOD); + javaFile.addMethod(REMOVE_CONTEXT_VALUE_NAMED_METHOD); + javaFile.addMethod(GET_PARENT_CONTAINER_METHOD); + javaFile.addMethod(GET_PARENT_CONTAINER_MORE_METHOD); + + // JAXXValidator + javaFile.addField(VALIDATOR_IDS_FIELD); + javaFile.addMethod(GET_VALIDATOR_METHOD); + + // PropertyChangeSupport + addPropertyChangeSupport(root); + + // DataBinding + javaFile.addMethod(PROCESS_DATA_BINDING_METHOD); } - javaFile.addImport("jaxx.runtime.validator.BeanValidator"); - javaFile.addImport("jaxx.runtime.JAXXInitialContext"); + javaFile.addImport(BeanValidator.class); + //javaFile.addImport(JAXXInitialContext.class); + javaFile.addField(ALL_COMPONENTS_CREATED_FIELD); + javaFile.addField(createJAXXObjectDescriptorField()); + if (compiler.getStylesheet() != null) { - javaFile.addField(new JavaField(0, "java.util.Map", "$previousValues", "new java.util.HashMap()")); + javaFile.addField(PREVIOUS_VALUES_FIELD); } - javaFile.addMethod(createConstructor(className)); - javaFile.addMethod(createConstructorWithInitialContext(className)); - javaFile.addMethod(createInitializer(className)); - javaFile.addMethod(new JavaMethod(Modifier.PUBLIC, "BeanValidator<?>", "getValidator", new JavaArgument[]{new JavaArgument("String", "validatorId")}, - null, "return (BeanValidator)(validatorIds.contains(validatorId)?getObjectById(validatorId):null);")); + javaFile.addMethod(createConstructor(className, superclassIsJAXXObject, jaxxContextImplementorClass)); + javaFile.addMethod(createConstructorWithInitialContext(className, superclassIsJAXXObject, jaxxContextImplementorClass)); + javaFile.addMethod(createInitializer()); + javaFile.addMethod(GET_JAXX_OBJECT_DESCRIPTOR_METHOD); + // DataBinding for (DataBinding dataBinding : compiler.getDataBindings()) { if (dataBinding.compile(true)) { initDataBindings.append("applyDataBinding(").append(TypeManager.getJavaCode(dataBinding.getId())).append(");").append(JAXXCompiler.getLineSeparator()); @@ -161,83 +202,59 @@ javaFile.addBodyCode(bodyCode.toString()); - for (CompiledObject object : objects.values()) { - if (!compiler.inlineCreation(object) && object != root) { - javaFile.addMethod(new JavaMethod(Modifier.PROTECTED, "void", object.getCreationMethodName(), null, null, getCreationCode(object))); + javaFile.addMethod(createCompleteSetupMethod(initDataBindings)); + + if (superclassIsJAXXObject) { + boolean hasBind = applyDataBinding.length() > 0; + if (hasBind) { + applyDataBinding.append(" else {"); + applyDataBinding.append(JAXXCompiler.getLineSeparator()); + applyDataBinding.append(" "); } - } + applyDataBinding.append("super.applyDataBinding($binding);"); + applyDataBinding.append(JAXXCompiler.getLineSeparator()); - javaFile.addField(new JavaField(Modifier.PRIVATE, "boolean", "allComponentsCreated")); + if (hasBind) { + applyDataBinding.append(" return;"); + applyDataBinding.append(JAXXCompiler.getLineSeparator()); + applyDataBinding.append("}"); + } - javaFile.addMethod(createCompleteSetupMethod()); - javaFile.addMethod(new JavaMethod(Modifier.PUBLIC, "void", "applyDataBinding", new JavaArgument[]{new JavaArgument("String", "$binding")}, - null, applyDataBinding.toString() + JAXXCompiler.getLineSeparator() + " processDataBinding($binding);")); + hasBind = removeDataBinding.length() > 0; + if (hasBind) { + removeDataBinding.append(" else {"); + removeDataBinding.append(JAXXCompiler.getLineSeparator()); + removeDataBinding.append(" "); + } + removeDataBinding.append("super.removeDataBinding($binding);"); + removeDataBinding.append(JAXXCompiler.getLineSeparator()); - javaFile.addMethod(new JavaMethod(Modifier.PUBLIC, "void", "removeDataBinding", new JavaArgument[]{new JavaArgument("String", "$binding")}, - null, removeDataBinding.toString())); - - javaFile.addMethod(new JavaMethod(Modifier.PUBLIC, "void", "processDataBinding", new JavaArgument[]{new JavaArgument("String", "dest")}, - null, "processDataBinding(dest, false);")); - - javaFile.addMethod(createProcessDataBindingMethod()); - - javaFile.addField(createJAXXObjectDescriptorField()); - javaFile.addMethod(createGetJAXXObjectDescriptorMethod()); - - if (!superclassIsJAXXObject) { - javaFile.addField(createObjectMap()); - javaFile.addMethod(createGetObjectByIdMethod()); - - /* - * Gestion du context - */ - javaFile.addField(createContextField(compiler.getOptions().getJaxxContextImplementorClass())); - javaFile.addMethod(createSetContextValueMethod()); - javaFile.addMethod(createSetContextValueNameMethod()); - javaFile.addMethod(createGetContextValueMethod()); - javaFile.addMethod(createGetContextValueNameMethod()); - javaFile.addMethod(createGetParentContainer()); - javaFile.addMethod(createGetParentContainerMore()); + if (hasBind) { + removeDataBinding.append("}"); + } } - ClassDescriptor currentClass = root.getObjectClass(); - MethodDescriptor firePropertyChange = null; - while (firePropertyChange == null && currentClass != null) { - try { - firePropertyChange = currentClass.getDeclaredMethodDescriptor("firePropertyChange", new ClassDescriptor[]{ - ClassDescriptorLoader.getClassDescriptor(String.class), - ClassDescriptorLoader.getClassDescriptor(Object.class), - ClassDescriptorLoader.getClassDescriptor(Object.class) - }); - } - catch (NoSuchMethodException e) { - currentClass = currentClass.getSuperclass(); - } - } + javaFile.addMethod(newMethod(Modifier.PUBLIC, "void", "applyDataBinding", + applyDataBinding.toString() + JAXXCompiler.getLineSeparator() + "processDataBinding($binding);", + new JavaArgument("String", "$binding")) + ); - int modifiers = firePropertyChange != null ? firePropertyChange.getModifiers() : 0; - if (Modifier.isPublic(modifiers)) { - // we have all the support we need - } - if (Modifier.isProtected(modifiers)) { - // there is property change support but the firePropertyChange method is protected - javaFile.addMethod(new JavaMethod(Modifier.PUBLIC, "void", "firePropertyChange", new JavaArgument[]{ - new JavaArgument("java.lang.String", "propertyName"), new JavaArgument("java.lang.Object", "oldValue"), new JavaArgument("java.lang.Object", "newValue")}, - null, "super.firePropertyChange(propertyName, oldValue, newValue);")); - } else { - // either no support at all or firePropertyChange isn't accessible - addPropertyChangeSupport(javaFile); - } + javaFile.addMethod(newMethod(Modifier.PUBLIC, "void", "removeDataBinding", + removeDataBinding.toString(), new JavaArgument("String", "$binding")) + ); + javaFile.addMethod(createProcessDataBindingMethod(superclassIsJAXXObject)); + addEventHandlers(javaFile); if (ClassDescriptorLoader.getClassDescriptor(Application.class).isAssignableFrom(root.getObjectClass()) && !isMainDeclared()) { // TODO: check for existing main method first - javaFile.addMethod(new JavaMethod(Modifier.PUBLIC | Modifier.STATIC, "void", "main", - new JavaArgument[]{new JavaArgument("String[]", "arg")}, null, - "SwingUtilities.invokeLater(new Runnable() { public void run() { new " + className + "().setVisible(true); } });")); + javaFile.addMethod(newMethod(Modifier.PUBLIC | Modifier.STATIC, "void", "main", + "SwingUtilities.invokeLater(new Runnable() { public void run() { new " + className + "().setVisible(true); } });", + new JavaArgument("String[]", "arg")) + ); } } @@ -245,19 +262,6 @@ /*-- Create fields ----------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/ - private JavaField createObjectMap() { - return new JavaField(Modifier.PROTECTED, "Map<String,Object>", "$objectMap", "new HashMap<String,Object>()"); - } - - private JavaField createContextField(String jaxxContextImplementorClass) { - return new JavaField(Modifier.PROTECTED, "jaxx.runtime.JAXXContext", "delegateContext", "new "+jaxxContextImplementorClass+"(this);"); - //return new JavaField(Modifier.PROTECTED, "jaxx.runtime.JAXXContext", "delegateContext", "new jaxx.runtime.DefaultJAXXContext(this);"); - } - - private JavaField createLoggerField(String className) { - return new JavaField(Modifier.PUBLIC + Modifier.STATIC + Modifier.FINAL, "Log", "log", "LogFactory.getLog(" + className + ".class)"); - } - protected JavaField createJAXXObjectDescriptorField() { try { JAXXObjectDescriptor descriptor = compiler.getJAXXObjectDescriptor(); @@ -275,7 +279,7 @@ int sizeLimit = 65000; // constant strings are limited to 64K, and I'm not brave enough to push right up to the limit if (data.length() < sizeLimit) { - return new JavaField(Modifier.PRIVATE | Modifier.STATIC, "java.lang.String", "$jaxxObjectDescriptor", TypeManager.getJavaCode(data)); + return newField(Modifier.PRIVATE | Modifier.STATIC, "java.lang.String", "$jaxxObjectDescriptor", TypeManager.getJavaCode(data)); } else { StringBuffer initializer = new StringBuffer(); for (int i = 0; i < data.length(); i += sizeLimit) { @@ -287,7 +291,7 @@ } initializer.append("String.valueOf(").append(name).append(")"); } - return new JavaField(Modifier.PRIVATE | Modifier.STATIC, "java.lang.String", "$jaxxObjectDescriptor", initializer.toString()); + return newField(Modifier.PRIVATE | Modifier.STATIC, "java.lang.String", "$jaxxObjectDescriptor", initializer.toString()); } } catch (IOException e) { throw new RuntimeException("Internal error: can't-happen error", e); @@ -298,85 +302,42 @@ /*-- Create methods ---------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/ - protected JavaMethod createGetJAXXObjectDescriptorMethod() { - return new JavaMethod(Modifier.PUBLIC | Modifier.STATIC, "jaxx.runtime.JAXXObjectDescriptor", "$getJAXXObjectDescriptor", - null, null, "return jaxx.runtime.Util.decodeCompressedJAXXObjectDescriptor($jaxxObjectDescriptor);"); - } + protected void addPropertyChangeSupport(CompiledObject root) { + ClassDescriptor currentClass = root.getObjectClass(); + MethodDescriptor firePropertyChange = null; + while (firePropertyChange == null && currentClass != null) { + try { + firePropertyChange = currentClass.getDeclaredMethodDescriptor("firePropertyChange", new ClassDescriptor[]{ + ClassDescriptorLoader.getClassDescriptor(String.class), + ClassDescriptorLoader.getClassDescriptor(Object.class), + ClassDescriptorLoader.getClassDescriptor(Object.class) + }); - protected JavaMethod createGetObjectByIdMethod() { - return new JavaMethod(Modifier.PUBLIC, "java.lang.Object", "getObjectById", - new JavaArgument[]{new JavaArgument("String", "id")}, null, - "return $objectMap.get(id);"); - } + } + catch (NoSuchMethodException e) { + currentClass = currentClass.getSuperclass(); + } + } - private JavaMethod createSetContextValueMethod() { - - return new JavaMethod(Modifier.PUBLIC, "void", "setContextValue", - new JavaArgument[]{new JavaArgument("Object", "clazz")}, null, - "delegateContext.setContextValue(clazz, null);"); + int modifiers = firePropertyChange != null ? firePropertyChange.getModifiers() : 0; + if (Modifier.isPublic(modifiers)) { + // we have all the support we need + } + if (Modifier.isProtected(modifiers)) { + // there is property change support but the firePropertyChange method is protected + javaFile.addMethod(FIRE_PROPERTY_CHANGE_METHOD); + } else { + // either no support at all or firePropertyChange isn't accessible + javaFile.addField(PROPERTY_CHANGE_SUPPORT_FIELD); + javaFile.addMethod(GET_PROPERTY_CHANGE_SUPPORT_METHOD); + javaFile.addMethod(ADD_PROPERTY_CHANGE_SUPPORT_METHOD); + javaFile.addMethod(ADD_PROPERTY_CHANGE_SUPPORT_NAMED_METHOD); + javaFile.addMethod(REMOVE_PROPERTY_CHANGE_SUPPORT_METHOD); + javaFile.addMethod(REMOVE_PROPERTY_CHANGE_SUPPORT_NAMED_METHOD); + javaFile.addMethod(FIRE_PROPERTY_CHANGE_NAMED_METHOD); + } } - private JavaMethod createSetContextValueNameMethod() { - return new JavaMethod(Modifier.PUBLIC, "void", "setContextValue", - new JavaArgument[]{new JavaArgument("Object", "clazz"), new JavaArgument("String", "name")}, null, - "delegateContext.setContextValue(clazz, name);"); - } - - private JavaMethod createGetContextValueMethod() { - return new JavaMethod(Modifier.PUBLIC, "<T> T", "getContextValue", - new JavaArgument[]{new JavaArgument("Class<T>", "clazz")}, null, - "return delegateContext.getContextValue(clazz, null);"); - } - - private JavaMethod createGetContextValueNameMethod() { - return new JavaMethod(Modifier.PUBLIC, "<T> T", "getContextValue", - new JavaArgument[]{new JavaArgument("Class<T>", "clazz"), new JavaArgument("String", "name")}, null, - "return delegateContext.getContextValue(clazz, name);"); - } - - private JavaMethod createGetParentContainer() { - return new JavaMethod(Modifier.PUBLIC, "<O extends Container> O", "getParentContainer", - new JavaArgument[]{new JavaArgument("Class<O>", "clazz")}, null, - "return delegateContext.getParentContainer(clazz);"); - } - - private JavaMethod createGetParentContainerMore() { - return new JavaMethod(Modifier.PUBLIC, "<O extends Container> O", "getParentContainer", - new JavaArgument[]{new JavaArgument("Object", "source"), new JavaArgument("Class<O>", "clazz")}, null, - "return delegateContext.getParentContainer(source, clazz);"); - } - - - protected void addPropertyChangeSupport(JavaFile javaFile) throws CompilerException { - javaFile.addField(new JavaField(0, "java.beans.PropertyChangeSupport", "$propertyChangeSupport")); - - javaFile.addMethod(new JavaMethod(0, "java.beans.PropertyChangeSupport", "$getPropertyChangeSupport", null, null, - "if ($propertyChangeSupport == null)\n" + - " $propertyChangeSupport = new PropertyChangeSupport(this);\n" + - "return $propertyChangeSupport;")); - - javaFile.addMethod(new JavaMethod(Modifier.PUBLIC, "void", "addPropertyChangeListener", new JavaArgument[]{ - new JavaArgument("java.beans.PropertyChangeListener", "listener")}, null, - "$getPropertyChangeSupport().addPropertyChangeListener(listener);")); - - javaFile.addMethod(new JavaMethod(Modifier.PUBLIC, "void", "addPropertyChangeListener", new JavaArgument[]{ - new JavaArgument("java.lang.String", "property"), new JavaArgument("java.beans.PropertyChangeListener", "listener")}, null, - "$getPropertyChangeSupport().addPropertyChangeListener(property, listener);")); - - javaFile.addMethod(new JavaMethod(Modifier.PUBLIC, "void", "removePropertyChangeListener", new JavaArgument[]{ - new JavaArgument("java.beans.PropertyChangeListener", "listener")}, null, - "$getPropertyChangeSupport().removePropertyChangeListener(listener);")); - - javaFile.addMethod(new JavaMethod(Modifier.PUBLIC, "void", "removePropertyChangeListener", new JavaArgument[]{ - new JavaArgument("java.lang.String", "property"), new JavaArgument("java.beans.PropertyChangeListener", "listener")}, null, - "$getPropertyChangeSupport().removePropertyChangeListener(property, listener);")); - - javaFile.addMethod(new JavaMethod(Modifier.PUBLIC, "void", "firePropertyChange", new JavaArgument[]{ - new JavaArgument("java.lang.String", "propertyName"), new JavaArgument("java.lang.Object", "oldValue"), new JavaArgument("java.lang.Object", "newValue")}, - null, "$getPropertyChangeSupport().firePropertyChange(propertyName, oldValue, newValue);")); - } - - protected void addEventHandlers(JavaFile javaFile) { for (Map.Entry<String, Map<ClassDescriptor, List<EventHandler>>> e1 : compiler.getEventHandlers().entrySet()) { // outer loop is iterating over different objects (well, technically, different Java expressions) @@ -389,43 +350,62 @@ if (listenerMethod.getParameterTypes().length != 1) { throw new CompilerException("Expected event handler " + listenerMethod.getName() + " of class " + handler.getListenerClass() + " to have exactly one argument"); } - javaFile.addMethod(new JavaMethod(Modifier.PUBLIC, "void", methodName, - new JavaArgument[]{new JavaArgument(JAXXCompiler.getCanonicalName(listenerMethod.getParameterTypes()[0]), "event")}, null, - handler.getJavaCode())); + javaFile.addMethod(newMethod(Modifier.PUBLIC, "void", methodName, handler.getJavaCode(), + new JavaArgument(JAXXCompiler.getCanonicalName(listenerMethod.getParameterTypes()[0]), "event"))); } } } } - protected JavaMethod createConstructor(String className) throws CompilerException { + protected JavaMethod createConstructor(String className, boolean superclassIsJAXXObject, String jaxxContextImplementorClass) throws CompilerException { StringBuffer code = new StringBuffer(); String constructorParams = compiler.getRootObject().getConstructorParams(); if (constructorParams != null) { - code.append(" super(").append(constructorParams).append(");"); + code.append(" super(").append(constructorParams).append(");").append(JAXXCompiler.getLineSeparator()); + } else { + if (superclassIsJAXXObject) { + code.append(" super();").append(JAXXCompiler.getLineSeparator()); + } + } + if (!superclassIsJAXXObject) { + code.append("delegateContext = new ").append(jaxxContextImplementorClass).append("(this);"); code.append(JAXXCompiler.getLineSeparator()); } code.append("$initialize();"); code.append(JAXXCompiler.getLineSeparator()); - return new JavaMethod(Modifier.PUBLIC, null, className, null, null, code.toString()); + return newMethod(Modifier.PUBLIC, null, className, code.toString()); } - protected JavaMethod createConstructorWithInitialContext(String className) throws CompilerException { + protected JavaMethod createConstructorWithInitialContext(String className, boolean superclassIsJAXXObject, String jaxxContextImplementorClass) throws CompilerException { StringBuffer code = new StringBuffer(); String constructorParams = compiler.getRootObject().getConstructorParams(); if (constructorParams != null) { - code.append(" super(").append(constructorParams).append(");"); + code.append(" super(").append(constructorParams).append(");").append(JAXXCompiler.getLineSeparator()); + } else { + if (superclassIsJAXXObject) { + code.append(" super(parentContext);").append(JAXXCompiler.getLineSeparator()); + } + } + if (!superclassIsJAXXObject) { + code.append("delegateContext = new ").append(jaxxContextImplementorClass).append("(this);"); code.append(JAXXCompiler.getLineSeparator()); + code.append("if (parentContext instanceof jaxx.runtime.JAXXInitialContext) {"); + code.append(JAXXCompiler.getLineSeparator()); + code.append(" ((jaxx.runtime.JAXXInitialContext)parentContext).to(this);"); + code.append(JAXXCompiler.getLineSeparator()); + code.append("} else {"); + code.append(JAXXCompiler.getLineSeparator()); + code.append(" setContextValue(parentContext);"); + code.append(JAXXCompiler.getLineSeparator()); + code.append("}"); + code.append(JAXXCompiler.getLineSeparator()); } - - code.append("initialContext.to(this);"); - code.append(JAXXCompiler.getLineSeparator()); code.append("$initialize();"); code.append(JAXXCompiler.getLineSeparator()); - JavaArgument arg = new JavaArgument("jaxx.runtime.JAXXInitialContext", "initialContext"); - return new JavaMethod(Modifier.PUBLIC, null, className, new JavaArgument[]{arg}, null, code.toString()); + return newMethod(Modifier.PUBLIC, null, className, code.toString(), new JavaArgument("jaxx.runtime.JAXXContext", "parentContext")); } - protected JavaMethod createInitializer(String className) throws CompilerException { + protected JavaMethod createInitializer() throws CompilerException { StringBuffer code = new StringBuffer(); CompiledObject root = compiler.getRootObject(); code.append("$objectMap.put(").append(TypeManager.getJavaCode(root.getId())).append(", this);"); @@ -462,11 +442,11 @@ } code.append("$completeSetup();"); code.append(JAXXCompiler.getLineSeparator()); - return new JavaMethod(Modifier.PRIVATE, "void", "$initialize", null, null, code.toString()); + return newMethod(Modifier.PRIVATE, "void", "$initialize", code.toString()); } - protected JavaMethod createCompleteSetupMethod() { + protected JavaMethod createCompleteSetupMethod(StringBuffer initDataBindings) { StringBuffer code = new StringBuffer(); code.append("allComponentsCreated = true;"); code.append(JAXXCompiler.getLineSeparator()); @@ -478,8 +458,8 @@ String additionCode = object.getAdditionCode(); if (additionCode.length() > 0) { code.append(object.getAdditionMethodName()).append("();").append(JAXXCompiler.getLineSeparator()); - additionCode = "if (allComponentsCreated) {" + JAXXCompiler.getLineSeparator() + additionCode + "}"; - javaFile.addMethod(new JavaMethod(Modifier.PROTECTED, "void", object.getAdditionMethodName(), null, null, additionCode)); + additionCode = "if (!allComponentsCreated) {" + JAXXCompiler.getLineSeparator() + " return;" + JAXXCompiler.getLineSeparator() + "}" + JAXXCompiler.getLineSeparator() + additionCode; + javaFile.addMethod(newMethod(Modifier.PROTECTED, "void", object.getAdditionMethodName(), additionCode)); } } //code.append(getLineSeparator()); @@ -504,53 +484,57 @@ code.append(JAXXCompiler.getLineSeparator()); } } - return new JavaMethod(Modifier.PRIVATE, "void", "$completeSetup", null, null, code.toString()); + return newMethod(Modifier.PRIVATE, "void", "$completeSetup", code.toString()); } - protected JavaMethod createProcessDataBindingMethod() { + protected JavaMethod createProcessDataBindingMethod(boolean superclassIsJAXXObject) { StringBuffer code = new StringBuffer(); - boolean superclassIsJAXXObject = ClassDescriptorLoader.getClassDescriptor(JAXXObject.class).isAssignableFrom(compiler.getRootObject().getObjectClass()); + //boolean superclassIsJAXXObject = ClassDescriptorLoader.getClassDescriptor(JAXXObject.class).isAssignableFrom(compiler.getRootObject().getObjectClass()); // the force parameter forces the update to happen even if it is already in activeBindings. This // is used on superclass invocations b/c by the time the call gets to the superclass, it is already // marked active and would otherwise be skipped if (processDataBinding.length() > 0) { - code.append(" if (!$force && $activeBindings.contains($dest)) return;"); + code.append(" if (!$force && $activeBindings.contains($dest)) { "); code.append(JAXXCompiler.getLineSeparator()); - code.append(" $activeBindings.add($dest);"); + code.append(" return;"); code.append(JAXXCompiler.getLineSeparator()); - code.append(" try {"); + code.append("}"); code.append(JAXXCompiler.getLineSeparator()); + code.append("$activeBindings.add($dest);"); + code.append(JAXXCompiler.getLineSeparator()); + code.append("try {"); + code.append(JAXXCompiler.getLineSeparator()); if (processDataBinding.length() > 0) { code.append(processDataBinding); - code.append(JAXXCompiler.getLineSeparator()); + //code.append(JAXXCompiler.getLineSeparator()); } if (superclassIsJAXXObject) { - code.append(" else"); + code.append(" else {"); code.append(JAXXCompiler.getLineSeparator()); - code.append(" super.processDataBinding($dest, true);"); + code.append(" super.processDataBinding($dest, true);"); code.append(JAXXCompiler.getLineSeparator()); + code.append(" }"); + code.append(JAXXCompiler.getLineSeparator()); } - code.append(" }"); + code.append("} finally {"); code.append(JAXXCompiler.getLineSeparator()); - code.append(" finally {"); + code.append(" $activeBindings.remove($dest);"); code.append(JAXXCompiler.getLineSeparator()); - code.append(" $activeBindings.remove($dest);"); + code.append("}"); code.append(JAXXCompiler.getLineSeparator()); - code.append(" }"); - code.append(JAXXCompiler.getLineSeparator()); } else if (superclassIsJAXXObject) { - code.append(" super.processDataBinding($dest, true);"); + code.append("super.processDataBinding($dest, true);"); code.append(JAXXCompiler.getLineSeparator()); } - return new JavaMethod(Modifier.PUBLIC, "void", "processDataBinding", - new JavaArgument[]{new JavaArgument("String", "$dest"), new JavaArgument("boolean", "$force")}, - null, code.toString()); + return newMethod(Modifier.PUBLIC, "void", "processDataBinding", code.toString(), + new JavaArgument("String", "$dest"), new JavaArgument("boolean", "$force") + ); } /*---------------------------------------------------------------------------------*/ /*-- Create methods code ----------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/ - + protected String getCreationCode(CompiledObject object) throws CompilerException { if (object instanceof ScriptInitializer) { return object.getInitializationCode(compiler); Added: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/JAXXObjectGeneratorConstants.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/JAXXObjectGeneratorConstants.java (rev 0) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/JAXXObjectGeneratorConstants.java 2008-11-16 17:44:15 UTC (rev 1017) @@ -0,0 +1,164 @@ +package jaxx.compiler; + +import static java.lang.reflect.Modifier.FINAL; +import static java.lang.reflect.Modifier.PRIVATE; +import static java.lang.reflect.Modifier.PROTECTED; +import static java.lang.reflect.Modifier.PUBLIC; +import static java.lang.reflect.Modifier.STATIC; + +/** @author chemit */ +public class JAXXObjectGeneratorConstants { + + // fields + + protected static final JavaField ACTIVE_BINDINGS_FIELD = newField(PROTECTED, + "java.util.List<Object>", "$activeBindings", "new ArrayList<Object>()" + ); + + protected static final JavaField BINDING_SOURCES_FIELD = newField(PROTECTED, + "java.util.Map<String,Object>", "$bindingSources", "new HashMap<String,Object>()" + ); + + protected static final JavaField VALIDATOR_IDS_FIELD = newField(PROTECTED, + "java.util.List<String>", "validatorIds", "new ArrayList<String>()" + ); + + protected static final JavaField OBJECT_MAP_FIELD = newField(PROTECTED, + "Map<String,Object>", "$objectMap", "new HashMap<String,Object>()" + ); + + protected static final JavaField ALL_COMPONENTS_CREATED_FIELD = newField(PRIVATE, + "boolean", "allComponentsCreated" + ); + + protected static final JavaField PREVIOUS_VALUES_FIELD = newField(0, + "java.util.Map", "$previousValues", "new java.util.HashMap()" + ); + + protected static final JavaField DELEGATE_CONTEXT_FIELD = newField(PROTECTED | FINAL, + "jaxx.runtime.JAXXContext", "delegateContext" + ); + + protected static final JavaField PROPERTY_CHANGE_SUPPORT_FIELD = newField(0, + "java.beans.PropertyChangeSupport", "$propertyChangeSupport" + ); + + // JAXXContext methods + + protected static final JavaMethod GET_CONTEXT_VALUE_METHOD = newMethod(PUBLIC, "<T> T", "getContextValue", + "return delegateContext.getContextValue(clazz, null);", + new JavaArgument("Class<T>", "clazz") + ); + protected static final JavaMethod GET_CONTEXT_VALUE_NAMED_METHOD = newMethod(PUBLIC, "<T> T", "getContextValue", + "return delegateContext.getContextValue(clazz, name);", + new JavaArgument("Class<T>", "clazz"), new JavaArgument("String", "name") + ); + + protected static final JavaMethod SET_CONTEXT_VALUE_NAMED_METHOD = newMethod(PUBLIC, "<T> void", "setContextValue", + "delegateContext.setContextValue(o, name);", + new JavaArgument("T", "o"), new JavaArgument("String", "name") + ); + + protected static final JavaMethod SET_CONTEXT_VALUE_METHOD = newMethod(PUBLIC, "<T> void", "setContextValue", + "delegateContext.setContextValue(o, null);", + new JavaArgument("T", "o") + ); + + protected static final JavaMethod REMOVE_CONTEXT_VALUE_NAMED_METHOD = newMethod(PUBLIC, "<T> void", "removeContextValue", + "delegateContext.removeContextValue(clazz, name);", + new JavaArgument("Class<T>", "clazz"), new JavaArgument("String", "name") + ); + + protected static final JavaMethod REMOVE_CONTEXT_VALUE_METHOD = newMethod(PUBLIC, "<T> void", "removeContextValue", + "delegateContext.removeContextValue(clazz, null);", + new JavaArgument("Class<T>", "clazz") + ); + + protected static final JavaMethod GET_PARENT_CONTAINER_MORE_METHOD = newMethod(PUBLIC, "<O extends Container> O", "getParentContainer", + "return delegateContext.getParentContainer(source, clazz);", + new JavaArgument("Object", "source"), new JavaArgument("Class<O>", "clazz") + ); + + protected static final JavaMethod GET_PARENT_CONTAINER_METHOD = newMethod(PUBLIC, "<O extends Container> O", "getParentContainer", + "return delegateContext.getParentContainer(clazz);", + new JavaArgument("Class<O>", "clazz") + ); + + // JAXXObject methods + + protected static final JavaMethod GET_OBJECT_BY_ID_METHOD = newMethod(PUBLIC, "java.lang.Object", "getObjectById", + "return $objectMap.get(id);", + new JavaArgument("String", "id") + ); + + protected static final JavaMethod GET_JAXX_OBJECT_DESCRIPTOR_METHOD = newMethod(PUBLIC | STATIC, "jaxx.runtime.JAXXObjectDescriptor", "$getJAXXObjectDescriptor", + "return jaxx.runtime.Util.decodeCompressedJAXXObjectDescriptor($jaxxObjectDescriptor);" + ); + + protected static final JavaMethod PROCESS_DATA_BINDING_METHOD = newMethod(PUBLIC, "void", "processDataBinding", + "processDataBinding(dest, false);", + new JavaArgument("String", "dest") + ); + + // JAXXValidator methods + + protected static final JavaMethod GET_VALIDATOR_METHOD = newMethod(PUBLIC, "BeanValidator<?>", "getValidator", + "return (BeanValidator) (validatorIds.contains(validatorId)?getObjectById(validatorId):null);", + new JavaArgument("String", "validatorId") + ); + + // PropertyChangeSupport methods + + + protected static final JavaMethod FIRE_PROPERTY_CHANGE_METHOD = newMethod(PUBLIC, "void", "firePropertyChange", + "super.firePropertyChange(propertyName, oldValue, newValue);", + new JavaArgument("String", "propertyName"), new JavaArgument("Object", "oldValue"), new JavaArgument("Object", "newValue") + ); + + protected static final JavaMethod FIRE_PROPERTY_CHANGE_NAMED_METHOD = newMethod(PUBLIC, "void", "firePropertyChange", + "$getPropertyChangeSupport().firePropertyChange(propertyName, oldValue, newValue);", + new JavaArgument("String", "propertyName"), new JavaArgument("Object", "oldValue"), new JavaArgument("Object", "newValue") + ); + + protected static final JavaMethod GET_PROPERTY_CHANGE_SUPPORT_METHOD = newMethod(0, "java.beans.PropertyChangeSupport", "$getPropertyChangeSupport", + "if ($propertyChangeSupport == null)\n" + + " $propertyChangeSupport = new PropertyChangeSupport(this);\n" + + "return $propertyChangeSupport;"); + + protected static final JavaMethod ADD_PROPERTY_CHANGE_SUPPORT_METHOD = newMethod(PUBLIC, "void", "addPropertyChangeListener", + "$getPropertyChangeSupport().addPropertyChangeListener(listener);", + new JavaArgument("java.beans.PropertyChangeListener", "listener") + ); + + protected static final JavaMethod ADD_PROPERTY_CHANGE_SUPPORT_NAMED_METHOD = newMethod(PUBLIC, "void", "addPropertyChangeListener", + "$getPropertyChangeSupport().addPropertyChangeListener(property, listener);", + new JavaArgument("String", "property"), new JavaArgument("java.beans.PropertyChangeListener", "listener") + ); + + protected static final JavaMethod REMOVE_PROPERTY_CHANGE_SUPPORT_METHOD = newMethod(PUBLIC, "void", "removePropertyChangeListener", + "$getPropertyChangeSupport().removePropertyChangeListener(listener);", + new JavaArgument("java.beans.PropertyChangeListener", "listener") + ); + + protected static final JavaMethod REMOVE_PROPERTY_CHANGE_SUPPORT_NAMED_METHOD = newMethod(PUBLIC, "void", "removePropertyChangeListener", + "$getPropertyChangeSupport().removePropertyChangeListener(property, listener);", + new JavaArgument("String", "property"), new JavaArgument("java.beans.PropertyChangeListener", "listener") + ); + + + protected static JavaField newField(int modifiers, String returnType, String name) { + return newField(modifiers, returnType, name, null); + } + + protected static JavaField newField(int modifiers, String returnType, String name, String initializer) { + return new JavaField(modifiers, returnType, name, initializer); + } + + protected static JavaMethod newMethod(int modifiers, String returnType, String name, String initializer, String[] exceptions, JavaArgument... arguments) { + return new JavaMethod(modifiers, returnType, name, arguments, exceptions, initializer); + } + + protected static JavaMethod newMethod(int modifiers, String returnType, String name, String initializer, JavaArgument... arguments) { + return newMethod(modifiers, returnType, name, initializer, new String[0], arguments); + } +} Modified: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/JavaField.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/JavaField.java 2008-11-16 17:42:00 UTC (rev 1016) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/JavaField.java 2008-11-16 17:44:15 UTC (rev 1017) @@ -4,11 +4,15 @@ */ package jaxx.compiler; +import java.lang.reflect.Modifier; +import java.util.Comparator; + /** * Represents a field in a Java source file being generated for output. <code>JavaFields</code> are created * and added to a {@link JavaFile}, which can then output Java source code. */ -public class JavaField { +public class JavaField implements Comparable<JavaField> { + private int modifiers; private String type; private String name; @@ -90,20 +94,87 @@ public String toString() { StringBuffer result = new StringBuffer(); result.append(JavaFile.getModifiersText(modifiers)); - result.append(type); - result.append(' '); - result.append(name); + result.append(type).append(' ').append(name); if (initializer != null) { - result.append(" = "); - result.append(initializer); + result.append(" = ").append(initializer); } - result.append(';'); - result.append(' '); - if (java.lang.reflect.Modifier.isProtected(modifiers)) { - result.append("public ").append(type).append(" get").append(name.substring(0, 1).toUpperCase()).append(name.substring(1)).append("() {return ").append(name).append(";}"); - } else if (java.lang.reflect.Modifier.isPrivate(modifiers)) { - result.append("protected ").append(type).append(" get").append(name.substring(0, 1).toUpperCase()).append(name.substring(1)).append("() {return ").append(name).append(";}"); - } + result.append(';').append(JAXXCompiler.getLineSeparator()); return result.toString(); } + + public int compareTo(JavaField o) { + return COMPARATOR.compare(this, o); + } + + public static final Comparator<JavaField> COMPARATOR = new Comparator<JavaField>() { + + public int compare(JavaField o1, JavaField o2) { + + int result; + if ((result = compareStatic(o1, o2)) != 0) { + return result; + } + + // data sources must be on the last after all other fields + if ((result = compareDataSource(o1, o2)) != 0) { + return result; + } + + // same static + if ((result = compareVisibility(o1, o2)) != 0) { + return result; + } + // same visibility, test name + return o1.name.compareTo(o2.name); + } + + public int compareStatic(JavaField o1, JavaField o2) { + // first comparator modifiers : static always before none static + if (Modifier.isStatic(o1.modifiers) && !Modifier.isStatic(o2.modifiers)) { + return -1; + } + if (!Modifier.isStatic(o1.modifiers) && Modifier.isStatic(o2.modifiers)) { + return 1; + } + return 0; + } + + public int compareDataSource(JavaField o1, JavaField o2) { + // first comparator modifiers : static always before none static + if (o1.name.startsWith("$DataSource") && !o2.name.startsWith("$DataSource")) { + return 1; + } + if (!o1.name.startsWith("$DataSource") && o2.name.startsWith("$DataSource")) { + return -1; + } + return 0; + } + + public int compareVisibility(JavaField o1, JavaField o2) { + // first comparator modifiers : static always before none static + if (!Modifier.isPublic(o1.modifiers) && Modifier.isPublic(o2.modifiers)) { + return 1; + } + + if (Modifier.isPublic(o1.modifiers) && !Modifier.isPublic(o2.modifiers)) { + return -1; + } + + if (Modifier.isProtected(o1.modifiers) && !Modifier.isProtected(o2.modifiers)) { + return -1; + } + if (!Modifier.isProtected(o1.modifiers) && Modifier.isProtected(o2.modifiers)) { + return 1; + } + + if (Modifier.isPrivate(o1.modifiers) && !Modifier.isPrivate(o2.modifiers)) { + return -1; + } + if (!Modifier.isPrivate(o1.modifiers) && Modifier.isPrivate(o2.modifiers)) { + return 1; + } + return 0; + } + }; + } \ No newline at end of file Modified: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/JavaFile.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/JavaFile.java 2008-11-16 17:42:00 UTC (rev 1016) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/JavaFile.java 2008-11-16 17:44:15 UTC (rev 1017) @@ -14,6 +14,13 @@ * {@link #toString} method to generate source code for it. */ public class JavaFile { + + protected static final String GETTER_PATTERN = "return %1$s;"; + + protected static final String BOOLEAN_GETTER_PATTERN = "return %1$s !=null && %1$s;"; + + protected static final String SETTER_PATTERN = "%1$s oldValue = this.%2$s;\nthis.%2$s = newValue;\nfirePropertyChange(\"%2$s\", oldValue, newValue);"; + private int modifiers; private String className; private List<String> imports = new ArrayList<String>(); @@ -46,7 +53,11 @@ imports.add(importString); } + public void addImport(Class importString) { + imports.add(importString.getName()); + } + public String[] getImports() { return imports.toArray(new String[imports.size()]); } @@ -101,8 +112,34 @@ return methods.toArray(new JavaMethod[methods.size()]); } + public void addField(JavaField field) { + addField(field, false); + } - public void addField(JavaField field) { + public void addField(JavaField field, boolean javaBean) { + addSimpleField(field); + String id = field.getName(); + String capitalizedName = JAXXCompiler.capitalize(id); + + // add getter file + String content = String.format(GETTER_PATTERN, id); + addMethod(new JavaMethod( + Modifier.isProtected(field.getModifiers()) ? Modifier.PUBLIC : Modifier.PROTECTED, + field.getType(), "get" + capitalizedName, null, null, content)); + + if (javaBean) { + // add full javabean support + if (Boolean.class.getName().equals(field.getType())) { + content = String.format(BOOLEAN_GETTER_PATTERN, id); + addMethod(new JavaMethod(Modifier.PUBLIC, field.getType(), "is" + capitalizedName, null, null, content)); + } + content = String.format(SETTER_PATTERN, field.getType(), id); + JavaArgument arg = new JavaArgument(field.getType(), "newValue"); + addMethod(new JavaMethod(Modifier.PUBLIC, "void", "set" + capitalizedName, new JavaArgument[]{arg}, null, content)); + } + } + + public void addSimpleField(JavaField field) { fields.add(field); } @@ -123,15 +160,17 @@ public static String indent(String source, int indentation, boolean trim) { - if (trim) + if (trim) { source = source.trim(); + } char[] spaces = new char[indentation]; Arrays.fill(spaces, ' '); StringBuffer result = new StringBuffer(); String[] lines = source.split(System.getProperty("line.separator") + "|\n"); for (int i = 0; i < lines.length; i++) { - if (i > 0) + if (i > 0) { result.append(JAXXCompiler.getLineSeparator()); + } result.append(spaces); result.append(trim ? lines[i].trim() : lines[i]); } @@ -147,6 +186,8 @@ public String getClassBody() { StringBuffer result = new StringBuffer(); if (fields.size() > 0) { + java.util.Collections.sort(fields); // sort fields + for (JavaField field : fields) { result.append(addIndentation(field.toString(), 4)); result.append(JAXXCompiler.getLineSeparator()); @@ -172,6 +213,7 @@ result.append(JAXXCompiler.getLineSeparator()); result.append(JAXXCompiler.getLineSeparator()); } + java.util.Collections.sort(methods); // sort methods for (JavaMethod method : methods) { result.append(addIndentation(method.toString(), 4)); @@ -193,8 +235,9 @@ if (interfaces != null && interfaces.length > 0) { result.append(" implements "); for (int i = 0; i < interfaces.length; i++) { - if (i > 0) + if (i > 0) { result.append(", "); + } result.append(interfaces[i]); } } @@ -207,10 +250,11 @@ public static String getModifiersText(int modifiers) { - if (modifiers == 0) + if (modifiers == 0) { return ""; - else + } else { return Modifier.toString(modifiers) + ' '; + } } Modified: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/JavaMethod.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/JavaMethod.java 2008-11-16 17:42:00 UTC (rev 1016) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/compiler/JavaMethod.java 2008-11-16 17:44:15 UTC (rev 1017) @@ -4,13 +4,16 @@ */ package jaxx.compiler; +import java.lang.reflect.Modifier; +import java.util.Comparator; + /** * Represents a method in a Java source file being generated for output. <code>JavaMethods</code> are created * and added to a {@link JavaFile}, which can then output Java source code. In addition to normal methods, a * <code>JavaMethod</code> can represent a constructor -- constructors should be named after their containing * classes and have a return type of <code>null</code>. */ -public class JavaMethod { +public class JavaMethod implements Comparable<JavaMethod> { private int modifiers; private String returnType; private String name; @@ -161,10 +164,12 @@ * @param extraCode Java source code to append to the method's body */ public void appendBodyCode(String extraCode) { - if (extraCode.length() == 0) + if (extraCode.length() == 0) { return; - if (bodyCode.length() > 0 && !bodyCode.toString().endsWith(JAXXCompiler.getLineSeparator())) + } + if (bodyCode.length() > 0 && !bodyCode.toString().endsWith(JAXXCompiler.getLineSeparator())) { bodyCode.append(JAXXCompiler.getLineSeparator()); + } bodyCode.append(extraCode); } @@ -174,6 +179,7 @@ * * @return the Java source code for this method */ + @Override public String toString() { StringBuffer result = new StringBuffer(); result.append(JavaFile.getModifiersText(modifiers)); @@ -185,8 +191,9 @@ result.append('('); if (arguments != null) { for (int i = 0; i < arguments.length; i++) { - if (i > 0) + if (i > 0) { result.append(", "); + } result.append(arguments[i].toString()); } } @@ -202,4 +209,68 @@ result.append("}"); return result.toString(); } + + public int compareTo(JavaMethod o) { + return COMPARATOR.compare(this, o); + } + + public static final Comparator<JavaMethod> COMPARATOR = new Comparator<JavaMethod>() { + + public int compare(JavaMethod o1, JavaMethod o2) { + + int result; + if ((result = compareStatic(o1, o2)) != 0) { + return result; + } + if ((result = compareConstructor(o1, o2)) != 0) { + return result; + } + if ((result = compareVisibility(o1, o2)) != 0) { + return result; + } + return o1.name.compareTo(o2.name); + } + + public int compareStatic(JavaMethod o1, JavaMethod o2) { + if (Modifier.isStatic(o1.modifiers) && !Modifier.isStatic(o2.modifiers)) { + return -1; + } + if (!Modifier.isStatic(o1.modifiers) && Modifier.isStatic(o2.modifiers)) { + return 1; + } + return 0; + } + + public int compareConstructor(JavaMethod o1, JavaMethod o2) { + if (o1.returnType == null && o2.returnType != null) { + return -1; + } + if (o1.returnType != null && o2.returnType == null) { + return 1; + } + return 0; + } + + public int compareVisibility(JavaMethod o1, JavaMethod o2) { + if (Modifier.isPublic(o1.modifiers) && !Modifier.isPublic(o2.modifiers)) { + return -1; + } + if (!Modifier.isPublic(o1.modifiers) && Modifier.isPublic(o2.modifiers)) { + return 1; + } + if (Modifier.isProtected(o1.modifiers) && !Modifier.isProtected(o2.modifiers)) { + return -1; + } + if (!Modifier.isProtected(o1.modifiers) && Modifier.isProtected(o2.modifiers)) { + return 1; + } + if (Modifier.isPrivate(o1.modifiers) && !Modifier.isPrivate(o2.modifiers)) { + return -1; + } + if (!Modifier.isPrivate(o1.modifiers) && Modifier.isPrivate(o2.modifiers)) { + return 1; + } + return 0; + } + }; } \ No newline at end of file Added: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/Decorator.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/Decorator.java (rev 0) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/Decorator.java 2008-11-16 17:44:15 UTC (rev 1017) @@ -0,0 +1,16 @@ +package jaxx.runtime; + +/** + * A simple contract to define a String decorator on any java objet. + * + * @author chemit + */ +public interface Decorator<O> { + + /** + * @param bean the bean to decorate + * @return the string value of the given bean + */ + String toString(O bean); + +} Modified: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/DefaultJAXXContext.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/DefaultJAXXContext.java 2008-11-16 17:42:00 UTC (rev 1016) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/DefaultJAXXContext.java 2008-11-16 17:44:15 UTC (rev 1017) @@ -1,30 +1,43 @@ package jaxx.runtime; +import static jaxx.runtime.JAXXContextEntryDef.newDef; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.awt.Container; import java.util.HashMap; import java.util.Map; +import java.util.Map.Entry; /** - * The default {@link JAXXContext} to beused in a {@link JAXXObject} by delegation. + * The default {@link JAXXContext} to be used in a {@link JAXXObject} by delegation. + * <p/> + * The values are store in a {@link Map} but we can not use directly the values as key. + * <p/> + * Because, it does not work if we add for the same object multi entries (named and unamed)... + * <p/> + * We prefer use as entry the {@link JAXXContextEntryDef} associated with the value. * * @author chemit */ public class DefaultJAXXContext implements JAXXContext { + protected static final JAXXContextEntryDef<JAXXContext> PARENT_CONTEXT_ENTRY = newDef(JAXXContext.class); + /** to use log facility, just put in your code: log.info(\"...\"); */ static private final Log log = LogFactory.getLog(DefaultJAXXContext.class); /** l'ui auquel est rattache le context */ protected JAXXObject ui; + /** le context parent */ + protected JAXXContext parentContext; + /** les données contenues dans le context */ - protected final Map<Object, String> data; + protected final Map<JAXXContextEntryDef, Object> data; public DefaultJAXXContext() { - data = new HashMap<Object, String>(); + data = new HashMap<JAXXContextEntryDef, Object>(); } public DefaultJAXXContext(JAXXObject ui) { @@ -32,19 +45,25 @@ this.ui = ui; } - public void setContextValue(Object o) { + public <T> void setContextValue(T o) { setContextValue(o, null); } - public void setContextValue(Object o, String name) { - // first remove - Object toRemove = getContextValue(o.getClass(), name); - if (toRemove != null) { - log.info("remove previously value " + toRemove); - data.remove(toRemove); + public <T> void setContextValue(T o, String name) { + if (PARENT_CONTEXT_ENTRY.accept2(o.getClass(), null)) { + setParentContext((JAXXContext) o); + return; } + JAXXContextEntryDef<?> entry = getKey(name, o.getClass()); + // first remove entry + Object oldValue = remove0(o.getClass(), name); + if (oldValue != null) { + if (log.isDebugEnabled()) { + log.debug("remove value " + oldValue.getClass() + " for " + entry); + } + } // then can put safely - data.put(o, name); + data.put(entry, o); } public <T> T getContextValue(Class<T> clazz) { @@ -53,23 +72,22 @@ @SuppressWarnings({"unchecked"}) public <T> T getContextValue(Class<T> clazz, String name) { - for (Map.Entry<Object, String> entry : data.entrySet()) { - if (clazz.isAssignableFrom(entry.getKey().getClass())) { - if ((name == null && entry.getValue() == null) || - (name != null && name.equals(entry.getValue()))) { - return (T) entry.getKey(); - } + if (PARENT_CONTEXT_ENTRY.accept(clazz, name)) { + return (T) getParentContext(); + } + for (Entry<JAXXContextEntryDef, Object> entry : data.entrySet()) { + if (entry.getKey().accept(clazz, name)) { + return (T) entry.getValue(); } } // no value found in this context, will try in the parent context - if (JAXXContext.class == clazz) { // no seek in the parent context, since we are already looking for it return null; } - JAXXContext parentContext = getContextValue(JAXXContext.class); + JAXXContext parentContext = getParentContext(); if (parentContext == null) { // no parent context, so no value find return null; @@ -78,7 +96,14 @@ return parentContext.getContextValue(clazz, name); } + public <T> void removeContextValue(Class<T> klazz) { + removeContextValue(klazz, null); + } + public <T> void removeContextValue(Class<T> klazz, String name) { + remove0(klazz, name); + } + public <O extends Container> O getParentContainer(Class<O> clazz) { return this.getParentContainer(ui, clazz); } @@ -108,4 +133,56 @@ protected void setUi(JAXXObject ui) { this.ui = ui; } + + protected JAXXContextEntryDef<?> getKey(String name, Class<?> klass) { + return JAXXContextEntryDef.newDef(name, klass); + } + + @SuppressWarnings({"unchecked"}) + protected <T> T remove0(Class<T> klazz, String name) { + if (PARENT_CONTEXT_ENTRY.accept(klazz, name)) { + JAXXContext old = getParentContext(); + setParentContext(null); + return (T) old; + } + JAXXContextEntryDef entry = null; + for (JAXXContextEntryDef entryDef : data.keySet()) { + if (entryDef.accept(klazz, name)) { + entry = entryDef; + break; + } + } + if (entry != null) { + return (T) data.remove(entry); + } + + if (JAXXContext.class == klazz) { + return null; + } + // try in parentContext + JAXXContext parentContext = getParentContext(); + + if (parentContext == null) { + return null; + } + + if (parentContext instanceof DefaultJAXXContext) { + return ((DefaultJAXXContext) parentContext).remove0(klazz, name); + } + + return null; + } + + protected JAXXContext getParentContext() { + return parentContext; + } + + protected void setParentContext(JAXXContext parentContext) { + if (parentContext instanceof JAXXObject) { + // keep the real context, not the ui + parentContext = ((JAXXObject) parentContext).getDelegateContext(); + } + this.parentContext = parentContext; + } + } Modified: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/JAXXContext.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/JAXXContext.java 2008-11-16 17:42:00 UTC (rev 1016) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/JAXXContext.java 2008-11-16 17:44:15 UTC (rev 1017) @@ -31,7 +31,7 @@ * * @param o the value to push in context */ - public void setContextValue(Object o); + public <T> void setContextValue(T o); /** * * Push in the context a new amed entry. @@ -41,9 +41,25 @@ * @param o the value to push in context * @param name the name of the new entry */ - public void setContextValue(Object o, String name); + public <T> void setContextValue(T o, String name); /** + * Remove from context the value with the given klazz as an unamed entry + * + * @param klazz the klazz entry + */ + public <T> void removeContextValue(Class<T> klazz); + + /** + * Remove from context the value with the given klazz as an unamed (if name is null) or named entry + * + * @param klazz the klazz entry + * @param name extra name of the entry + */ + + public <T> void removeContextValue(Class<T> klazz, String name); + + /** * Seek for a unamed entry in the context * <p/> * This is an exemple to call a method in JAXX : Modified: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/JAXXContextEntryDef.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/JAXXContextEntryDef.java 2008-11-16 17:42:00 UTC (rev 1016) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/JAXXContextEntryDef.java 2008-11-16 17:44:15 UTC (rev 1017) @@ -32,8 +32,11 @@ return newListDef(null); } + @SuppressWarnings({"unchecked"}) public static <O> JAXXContextEntryDef<List<O>> newListDef(String name) { - return new JAXXContextEntryDef<List<O>>(name, JAXXContextEntryDef.<O>castList()); + JAXXContextEntryDef contextEntryDef = new JAXXContextEntryDef<List<O>>(name, JAXXContextEntryDef.<O>castList()); + contextEntryDef.klass = List.class; + return contextEntryDef; } @@ -71,4 +74,40 @@ return (Class<List<O>>) Collections.emptyList().getClass(); } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof JAXXContextEntryDef)) { + return false; + } + JAXXContextEntryDef that = (JAXXContextEntryDef) o; + return klass.equals(that.klass) && !(name != null ? !name.equals(that.name) : that.name != null); + + } + + @Override + public int hashCode() { + int result = (name != null ? name.hashCode() : 0); + return 31 * result + klass.hashCode(); + } + + public boolean accept(Class<?> klass, String name) { + if (klass == Object.class && this.klass != Object.class) { + // block if looking for Object.class + return false; + } + return klass.isAssignableFrom(this.klass) && (this.name == null && name == null + || (this.name != null && name != null && this.name.equals(name))); + } + + public boolean accept2(Class<?> klass, String name) { + if (klass == Object.class && this.klass != Object.class) { + // block if looking for Object.class + return false; + } + return this.klass.isAssignableFrom(klass) && (this.name == null && name == null + || (this.name != null && name != null && this.name.equals(name))); + } } Modified: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/JAXXInitialContext.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/JAXXInitialContext.java 2008-11-16 17:42:00 UTC (rev 1016) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/JAXXInitialContext.java 2008-11-16 17:44:15 UTC (rev 1017) @@ -52,9 +52,12 @@ * @param dst the object to fill. */ public void to(JAXXContext dst) { - for (Entry<Object, String> entry : data.entrySet()) { - dst.setContextValue(entry.getKey(), entry.getValue()); + if (parentContext != null) { + dst.setContextValue(parentContext); } + for (Entry<JAXXContextEntryDef, Object> entry : data.entrySet()) { + dst.setContextValue(entry.getValue(), entry.getKey().getName()); + } } @Override @@ -68,6 +71,16 @@ } @Override + public <T> void removeContextValue(Class<T> klazz) { + throw new RuntimeException("not implemented"); + } + + @Override + public <T> void removeContextValue(Class<T> klazz, String name) { + throw new RuntimeException("not implemented"); + } + + @Override public <O extends Container> O getParentContainer(Class<O> clazz) { throw new RuntimeException("not implemented"); } Modified: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/JAXXObject.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/JAXXObject.java 2008-11-16 17:42:00 UTC (rev 1016) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/JAXXObject.java 2008-11-16 17:44:15 UTC (rev 1017) @@ -26,6 +26,7 @@ public void removeDataBinding(String id); + public jaxx.runtime.JAXXContext getDelegateContext(); /** * Processes a data binding by name. Data binding names are comprised of an object ID and a property name: Modified: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/Util.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/Util.java 2008-11-16 17:42:00 UTC (rev 1016) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/Util.java 2008-11-16 17:44:15 UTC (rev 1017) @@ -305,6 +305,9 @@ } catch (IllegalAccessException e) { log.error("could not obtain beanInfo for " + valueClass.getClass() + ", reason : " + e.getMessage(), e); } + } else { + //fixme : conversion failed, we should be able to notify ui that values has changed ? + // otherwise, bean value has not changed,... } } @@ -430,4 +433,9 @@ } + public static void processDataBinding(JAXXObject src, String... bindings) { + for (String binding : bindings) { + src.processDataBinding(binding); + } + } } Added: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/swing/CardLayout2.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/swing/CardLayout2.java (rev 0) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/swing/CardLayout2.java 2008-11-16 17:44:15 UTC (rev 1017) @@ -0,0 +1,87 @@ +package jaxx.runtime.swing; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.awt.CardLayout; +import java.awt.Component; +import java.awt.Container; +import java.io.Serializable; +import java.util.LinkedList; +import java.util.List; + +/** + * An override of the awt {@link java.awt.CardLayout}. + * <p/> + * Because in the original layout is not overridable : everything is package level accessible. + * <p/> + * This new class offers to test if a constrains (as a Serializable) is actually dealed by the layout, + * via the method {@link #contains(java.io.Serializable)}. + * <p/> + * We had also another method to obtain the current visible component in a container layouted by the class, + * via the method {@link #getVisibleComponent(java.awt.Container)}. + * + * @author chemit + * @version 1.0 + */ +public class CardLayout2 extends CardLayout { + + /** log */ + static private Log log = LogFactory.getLog(CardLayout2.class); + + private static final long serialVersionUID = 1L; + + /** list of already loaded context (since the {@link #vector} attribute is package visible... */ + protected List<Serializable> contexts = new LinkedList<Serializable>(); + + @Override + public void addLayoutComponent(Component comp, Object constraints) { + super.addLayoutComponent(comp, constraints); + contexts.add((Serializable) constraints); + if (log.isDebugEnabled()) { + log.debug(this + " new constraints : " + constraints); + } + } + + /** + * Test if a constrains is contained in the layout. + * + * @param constraints l'identifiant a tester + * @return <code>true</code> si l'identifiant est deja present dans le layout, <code>false</code> autrement. + */ + public boolean contains(Serializable constraints) { + return contexts.contains(constraints); + } + + /** + * Obtain the visible component in the container. + * + * @param container the container using this layout + * @return the component visible in the container. + */ + public Component getVisibleComponent(Container container) { + if (container.getLayout() != this) { + throw new IllegalArgumentException("the container is not managed by the current layout"); + } + for (Component component : container.getComponents()) { + if (component.isVisible()) { + return component; + } + } + // no component actually visible + return null; + } + + public Component getComponent(Container container, String constraints) { + if (container.getLayout() != this) { + throw new IllegalArgumentException("the container is not manage by the current layout"); + } + if (!contexts.contains(constraints)) { + throw new IllegalArgumentException("the constraints '" + constraints + "' is not supported by this layout : " + contexts); + } + int index = contexts.indexOf(constraints); + return container.getComponents()[index]; + } + + +} Added: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/swing/NavigationTreeModel.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/swing/NavigationTreeModel.java (rev 0) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/swing/NavigationTreeModel.java 2008-11-16 17:44:15 UTC (rev 1017) @@ -0,0 +1,259 @@ +package jaxx.runtime.swing; + + +import jaxx.runtime.JAXXAction; +import jaxx.runtime.JAXXContext; +import jaxx.runtime.JAXXContextEntryDef; +import jaxx.runtime.JAXXObject; +import org.apache.commons.beanutils.BeanUtilsBean; +import org.apache.commons.beanutils.PropertyUtilsBean; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreeNode; +import java.lang.reflect.InvocationTargetException; +import java.util.List; +import java.util.Stack; + +/** + * Le modele utilisé pour un arbre de navigation. + * <p/> + * Il est composé de {@link NavigationTreeModel.NavigationTreeNode} + * + * @author chemit + */ +public class NavigationTreeModel extends DefaultTreeModel { + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static private Log log = LogFactory.getLog(NavigationTreeModel.class); + + /** la représentation d'un noeud dans le model {@link NavigationTreeModel} */ + public static class NavigationTreeNode extends DefaultMutableTreeNode { + + private static final long serialVersionUID = 1L; + + /** pour representer le context du noeud. */ + protected String context; + + /** the JAXXObject class associated with this node */ + protected Class<? extends JAXXObject> jaxxClass; + + /** the JAXXAction class associated with this node and will be put in ui context */ + protected Class<? extends JAXXAction> jaxxActionClass; + + /** the definition of the JAXXContext entry associated to this node, if null will seek in parent */ + protected JAXXContextEntryDef jaxxContextEntryDef; + + public NavigationTreeNode(String context, Class<? extends JAXXObject> jaxxClass, Class<? extends JAXXAction> jaxxActionClass, JAXXContextEntryDef jaxxContextEntryDef) { + this.context = context; + this.jaxxClass = jaxxClass; + this.jaxxActionClass = jaxxActionClass; + this.jaxxContextEntryDef = jaxxContextEntryDef; + } + + public NavigationTreeNode(Object userObject, String context, Class<? extends JAXXObject> jaxxClass, Class<? extends JAXXAction> jaxxActionClass, JAXXContextEntryDef jaxxContextEntryDef) { + super(userObject); + this.context = context; + this.jaxxClass = jaxxClass; + this.jaxxActionClass = jaxxActionClass; + this.jaxxContextEntryDef = jaxxContextEntryDef; + } + + public NavigationTreeNode(Object userObject, boolean allowsChildren, String context, Class<? extends JAXXObject> jaxxClass, Class<? extends JAXXAction> jaxxActionClass, JAXXContextEntryDef jaxxContextEntryDef) { + super(userObject, allowsChildren); + this.context = context; + this.jaxxClass = jaxxClass; + this.jaxxActionClass = jaxxActionClass; + this.jaxxContextEntryDef = jaxxContextEntryDef; + } + + public String getContext() { + return context; + } + + public void setContext(String context) { + this.context = context; + } + + public Class<? extends JAXXObject> getJaxxClass() { + return jaxxClass; + } + + public void setJaxxClass(Class<? extends JAXXObject> jaxxClass) { + this.jaxxClass = jaxxClass; + } + + public Class<? extends JAXXAction> getJaxxActionClass() { + return jaxxActionClass; + } + + public void setJaxxActionClass(Class<? extends JAXXAction> jaxxActionClass) { + this.jaxxActionClass = jaxxActionClass; + } + + public JAXXContextEntryDef getJaxxContextEntryDef() { + return jaxxContextEntryDef; + } + + public void setJaxxContextEntryDef(JAXXContextEntryDef jaxxContextEntryDef) { + this.jaxxContextEntryDef = jaxxContextEntryDef; + } + + public String getContextPath() { + TreeNode[] path = getPath(); + StringBuilder sb = new StringBuilder(); + for (TreeNode treeNode : path) { + NavigationTreeNode myNode = (NavigationTreeNode) treeNode; + sb.append('.').append(myNode.getContext()); + } + return sb.substring(1); + } + + @Override + public NavigationTreeNode getChildAt(int index) { + return (NavigationTreeNode) super.getChildAt(index); + } + + public NavigationTreeNode getChild(String context) { + for (int i = 0, max = getChildCount(); i < max; i++) { + NavigationTreeNode son = getChildAt(i); + if (context.equals(son.getContext())) { + return son; + } + } + return null; + } + + public NavigationTreeNode findNode(Stack<String> stack) { + if (log.isDebugEnabled()) { + log.debug(context + " : enter with " + stack); + } + if (stack.isEmpty()) { + return this; + } + // nextcontext to search + String currentPath = stack.pop(); + if (getContext().equals(currentPath)) { + // this node matchs + return findNode(stack); + } + // find the next matching son + NavigationTreeNode son = getChild(currentPath); + return son == null ? null : son.findNode(stack); + } + + public NavigationTreeNode getFirstAncestorWithDef() { + if (jaxxContextEntryDef != null) { + return this; + } + if (getParent() == null) { + return null; + } + return ((NavigationTreeNode) getParent()).getFirstAncestorWithDef(); + } + } + + @Override + public NavigationTreeNode getRoot() { + return (NavigationTreeNode) super.getRoot(); + } + + private static final long serialVersionUID = 1L; + + + public NavigationTreeModel(TreeNode root) { + super(root); + } + + public NavigationTreeModel(TreeNode root, boolean asksAllowsChildren) { + super(root, asksAllowsChildren); + } + + public NavigationTreeNode findNode(String path) { + String[] paths = path.split("\\."); + Stack<String> stack = new Stack<String>(); + for (int i = paths.length - 1; i > -1; i--) { + String s = paths[i]; + stack.push(s); + } + return getRoot().findNode(stack); + } + + /** + * @param context the context to seek + * @param contextPath the current context path of the node + * @return the value associated in context with the given context path + * @throws InvocationTargetException todo + * @throws NoSuchMethodException todo + * @throws IllegalAccessException todo + */ + public Object getJAXXContextValue(JAXXContext context, String contextPath) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException { + Object result; + NavigationTreeNode node = findNode(contextPath); + result = getJAXXContextValue(context, node); + return result; + } + + public Object getJAXXContextValue(JAXXContext context, NavigationTreeNode node) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException { + Object result; + if (node == null) { + return null; + } + if (node.getJaxxContextEntryDef() != null) { + // the node maps directly a value in context + result = node.getJaxxContextEntryDef().getContextValue(context); + return result; + } + // find the first ancestor node with a context def + NavigationTreeNode parentNode = node.getFirstAncestorWithDef(); + if (parentNode == null) { + // no parent found + return null; + } + + Object parentBean = getJAXXContextValue(context, parentNode); + if (parentBean == null) { + return null; + } + result = parentBean; + // descend from parentNode to node (says descending into parentBean following node contexts) + TreeNode[] path = node.getPath(); + PropertyUtilsBean propertyUtils = BeanUtilsBean.getInstance().getPropertyUtils(); + + int level = parentNode.getLevel() + 1; + + if (List.class.isAssignableFrom(parentBean.getClass())) { + // parent bean is a list, returnindexed node + NavigationTreeNode sonNode = (NavigationTreeNode) path[level]; + //TODO Finish it... + int index = parentNode.getIndex(sonNode); + result = ((List) parentBean).get(index); + level++; + } + + for (int i = level, length = path.length; i < length; i++) { + NavigationTreeNode treeNode = (NavigationTreeNode) path[i]; + + String propertyName = treeNode.getContext(); + Object sonBean = propertyUtils.getProperty(result, propertyName); + if (sonBean == null) { + // can be null (for example a leaf list) + result = null; + break; + } + if (!(List.class.isAssignableFrom(sonBean.getClass())) || treeNode.isLeaf() || i == length - 1) { + // simple property and last node + result = sonBean; + continue; + } + // indexed node + NavigationTreeNode sonNode = (NavigationTreeNode) path[++i]; + int index = treeNode.getIndex(sonNode); + result = propertyUtils.getIndexedProperty(result, propertyName, index); + } + return result; + } + +} Added: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/swing/NavigationTreeSelectionAdapter.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/swing/NavigationTreeSelectionAdapter.java (rev 0) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/swing/NavigationTreeSelectionAdapter.java 2008-11-16 17:44:15 UTC (rev 1017) @@ -0,0 +1,181 @@ +package jaxx.runtime.swing; + +import jaxx.runtime.JAXXObject; +import jaxx.runtime.JAXXAction; +import jaxx.runtime.swing.NavigationTreeModel.NavigationTreeNode; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.swing.JTree; +import javax.swing.event.TreeSelectionEvent; +import javax.swing.event.TreeSelectionListener; +import javax.swing.tree.TreePath; +import java.awt.Component; +import java.lang.reflect.InvocationTargetException; + +/** A {@link javax.swing.event.TreeSelectionListener} implementation@author chemit */ +public abstract class NavigationTreeSelectionAdapter implements TreeSelectionListener { + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static private final Log log = LogFactory.getLog(NavigationTreeSelectionAdapter.class); + + static public final String NAVIGATION_CONTEXT_PATH = "navigation-context-path"; + + /** la classe d'ui par defaut, associé à un noeud de l'arbe */ + protected Class<? extends JAXXObject> defaultUIClass; + + protected Class<? extends JAXXAction> defaultUIHandlerClass; + + /** l'ui contenant l'arbre de navigation */ + protected JAXXObject context; + + protected NavigationTreeSelectionAdapter(Class<? extends JAXXObject> defaultUIClass, Class<? extends JAXXAction> defaultUIHandlerClass, JAXXObject context) { + this.defaultUIClass = defaultUIClass; + this.defaultUIHandlerClass = defaultUIHandlerClass; + this.context = context; + } + + protected abstract NavigationTreeModel getNavigationTreeModel(); + + /** + * @return le composent actuellement visible associé au noeud courant ou au noeud précédent + * lors d'un changement de noeud. + */ + protected abstract Component getCurrentUI(); + + /** + * @param path le chemin du noeud selectionne dont on veut afficher l'ui associée + * @return l'ui associé au novueau noeud sélectionné + */ + protected abstract Component getNewUI(String path); + + /** + * @param event l'evenement de selection de noeud + * @param component le composent actuellement visible + * @return <code>true</code> si le composent a bien été fermé, <code>false</code> sinon + * @throws Exception if any + */ + protected abstract boolean closeUI(TreeSelectionEvent event, Component component) throws Exception; + + /** + * Instancie une nouvelle ui associé à un noeud de l'arbre de navigation + * + * @param node le noeud associé à l'ui à créer + * @return la nouvelle ui associée au noeud + * @throws Exception if any + */ + protected abstract Component createUI(NavigationTreeNode node) throws Exception; + + /** + * Ouvre l'ui associée au noeud sélectionné dans l'arbre de navigation. + * + * @param newUI l'ui associé au noeud sélectionné à ouvrir + * @param path le path dans le context de navigation + * @throws Exception if any + */ + protected abstract void openUI(Component newUI, NavigationTreeNode path) throws Exception; + + /** + * Retourne au noeud précdemment sélectionné dans l'arbre de navigation, avec la possibilité de notifier + * une erreure survenue. + * + * @param event l'évènement de changement de noeud sélectionné. + * @param e l'erreur recontrée (ou null si pas d"erreur) + */ + protected abstract void goBackToPreviousNode(TreeSelectionEvent event, Exception e); + + public void valueChanged(TreeSelectionEvent event) { + if (event.getOldLeadSelectionPath() != null && event.getOldLeadSelectionPath().equals(event.getPath())) { + // do not treate this if no path changed + return; + } + + try { + + NavigationTreeNode node = (NavigationTreeNode) event.getPath().getLastPathComponent(); + + if (node.getJaxxClass() == null) { + // no ui is associated with this node, display a empty content + node.setJaxxClass(defaultUIClass); + } + + if (node.getJaxxActionClass()==null) { + node.setJaxxActionClass(defaultUIHandlerClass); + } + + String path = node.getContextPath(); + + if (log.isDebugEnabled()) { + log.debug(path); + } + + Component newUI = getNewUI(path); + Component component = getCurrentUI(); + + if (newUI != null && newUI.equals(component)) { + // call back from goto back to previous node, do nothing + return; + } + + + if (!closeUI(event, component)) { + // previous ui was not closed, so reselect the previous node in navigation + goBackToPreviousNode(event, null); + // and quit + return; + } + + // now, we are free to open the ui associated with the selected node in navigation + + // before all, attach bean in context associated with the selected node in naivgation tree + attachBeanFromNodeToContext(node); + + + if (newUI == null) { + // instanciate a new ui associated with the selected node + newUI = createUI(node); + + } + + // save in context current node context path + context.setContextValue(node.getContextPath(), NAVIGATION_CONTEXT_PATH); + + // really open the ui associated with the selected node + openUI(newUI, node); + + } catch (Exception e) { + // if any error, go back to previvous node + goBackToPreviousNode(event, e); + } + + } + + protected void attachBeanFromNodeToContext(NavigationTreeNode node) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException { + + String path = node.getContextPath(); + + NavigationTreeModel navigationModel = getNavigationTreeModel(); + + //if (navigationModel != null) { + + Object data = navigationModel.getJAXXContextValue(context, path); + if (data != null) { + if (log.isInfoEnabled()) { + log.info("find data for contextPath <" + path + "> : " + data.getClass()); + } + context.setContextValue(data); + } + //} + } + + protected void returnToPreviousNode(JTree tree, TreeSelectionEvent event) { + // go back to previous node + // put in context a tag to not come back again here + TreePath oldPath = event.getOldLeadSelectionPath(); + //NavigationTreeNode oldNode = (NavigationTreeNode) oldPath.getLastPathComponent(); + if (oldPath != null) { + tree.setSelectionPath(oldPath); + } + } + +} \ No newline at end of file Added: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/swing/NavigationtreeSelectionAdapterWithCardLayout.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/swing/NavigationtreeSelectionAdapterWithCardLayout.java (rev 0) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/swing/NavigationtreeSelectionAdapterWithCardLayout.java 2008-11-16 17:44:15 UTC (rev 1017) @@ -0,0 +1,105 @@ +package jaxx.runtime.swing; + +import jaxx.runtime.JAXXAction; +import jaxx.runtime.JAXXContext; +import jaxx.runtime.JAXXInitialContext; +import jaxx.runtime.JAXXObject; +import jaxx.runtime.swing.NavigationTreeModel.NavigationTreeNode; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.swing.JPanel; +import javax.swing.event.TreeSelectionEvent; +import java.awt.Component; + +/** + * Simple {@link NavigationTreeSelectionAdapter} implementation with a {@link CardLayout2} to manage components to + * associated with tree's nodes. + * <p/> + * For each node, the ui associated has a constraints in a cardlayout which is the node context path. + * <p/> + * A single container managed by the cardlayout is used to display the components associated with tree's nodes. + * + * @author chemit + */ +public abstract class NavigationtreeSelectionAdapterWithCardLayout extends NavigationTreeSelectionAdapter { + + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static private final Log log = LogFactory.getLog(NavigationtreeSelectionAdapterWithCardLayout.class); + + /** + * All components associated with a tree's node is displayed in a single container. + * + * @return the containter of components + */ + protected abstract JPanel getContentContainer(); + + /** + * the cardlayout managing components associated with tree node. The constraints + * of each component is the node contextPath. + * + * @return the layout used to display components associated with tree's nodes. + */ + protected abstract CardLayout2 getContentLayout(); + + public NavigationtreeSelectionAdapterWithCardLayout(Class<? extends JAXXObject> defaultUIClass, Class<? extends JAXXAction> defaultUIHandlerClass, JAXXObject context) { + super(defaultUIClass, defaultUIHandlerClass, context); + + if (getContentContainer() == null) { + throw new IllegalArgumentException("could not have a null 'contentContainer' in ui " + context); + } + if (getContentLayout() == null) { + throw new IllegalArgumentException("could not have a null 'contentLayout' in ui " + context); + } + } + + protected Component getCurrentUI() { + CardLayout2 layout = getContentLayout(); + JPanel container = getContentContainer(); + return layout.getVisibleComponent(container); + } + + protected Component getNewUI(String path) { + CardLayout2 layout = getContentLayout(); + JPanel container = getContentContainer(); + return layout.contains(path) ? layout.getComponent(container, path) : null; + } + + protected void openUI(Component newUI, NavigationTreeNode node) throws Exception { + CardLayout2 layout = getContentLayout(); + JPanel container = getContentContainer(); + // switch layout + layout.show(container, node.getContextPath()); + } + + protected boolean closeUI(TreeSelectionEvent event, Component component) throws Exception { + // by default, we says that component was succesfull closed + return true; + } + + protected Component createUI(NavigationTreeNode node) throws Exception { + JAXXObject newUI; + + if (node.getJaxxActionClass() != null) { + JAXXAction action = node.getJaxxActionClass().newInstance(); + // init context with + JAXXInitialContext context = action.init(this.context); + // must instanciate the ui with an JAXXInitialContext + newUI = node.getJaxxClass().getConstructor(JAXXContext.class).newInstance(context); + } else { + if (log.isWarnEnabled()) { + log.warn("no action associated with ui " + node.getJaxxClass()); + } + // no action associated, just + newUI = node.getJaxxClass().getConstructor(JAXXContext.class).newInstance(this.context); + } + if (log.isDebugEnabled()) { + log.debug("instanciate new ui " + newUI); + } + + getContentContainer().add((Component) newUI, node.getContextPath()); + return (Component) newUI; + } +} + Modified: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/swing/Utils.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/swing/Utils.java 2008-11-16 17:42:00 UTC (rev 1016) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/swing/Utils.java 2008-11-16 17:44:15 UTC (rev 1017) @@ -56,4 +56,6 @@ c.setText(text); } } + + } Modified: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/validator/BeanValidator.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/validator/BeanValidator.java 2008-11-16 17:42:00 UTC (rev 1016) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/validator/BeanValidator.java 2008-11-16 17:44:15 UTC (rev 1017) @@ -135,16 +135,25 @@ */ public class BeanValidator<T> { + static public final String BEAN_PROERTY = "bean"; + + private static final String CONTEXT_NAME_PROPERTY = "contextName"; + static public final String VALID_PROERTY = "valid"; + static public final String CHANGED_PROERTY = "changed"; + /** to use log facility, just put in your code: log.info(\"...\"); */ static private final Log log = LogFactory.getLog(BeanValidator.class); + /** Constant name of the default ui renderer to be used. */ static private final Class<? extends AbstractBeanValidatorUI> DEFAULT_UI_CLASS = IconValidationUI.class; + /** delgate property change support */ protected PropertyChangeSupport pcs; protected ValidationAwareSupport validationSupport; + protected DelegatingValidatorContext validationContext; protected transient ActionValidatorManager validator; @@ -152,11 +161,14 @@ /** indique si le bean a ete modifie depuis son arrivee */ protected boolean changed = false; + /** state of the validator */ + protected boolean valid; + /** le bean a surveiller */ protected T bean = null; /** l'objet qui recoit les notifications de modification du bean */ - protected Listener l = new Listener(); + protected PropertyChangeListener l; /** permet de faire le lien en un champs du bean et l'objet qui permet de l'editer */ protected Map<String, JComponent> fieldRepresentation; @@ -173,24 +185,103 @@ /** map of conversion errors detected by this validator */ protected Map<String, String> conversionErrors; - /** state of the validator */ - protected boolean valid; + /** the validation named context */ + protected String contextName; + public BeanValidator() { pcs = new PropertyChangeSupport(this); validationSupport = new ValidationAwareSupport(); validationContext = new DelegatingValidatorContext(validationSupport); fieldRepresentation = new HashMap<String, JComponent>(); conversionErrors = new TreeMap<String, String>(); + l = new PropertyChangeListener() { + public void propertyChange(PropertyChangeEvent evt) { + // only validate if the property has really changed... + /*Object oldValue = evt.getOldValue(); + Object newValue = evt.getNewValue(); + if (oldValue == null && newValue == null) { + return; + } + if ((oldValue != null && oldValue.equals(newValue)) || (newValue != null && newValue.equals(oldValue))) { + return; + } + if (log.isDebugEnabled()) { + log.debug("launch validation from event [name:" + evt.getPropertyName() + " <old:" + oldValue + ", new:" + newValue + ">]"); + }*/ + validate(); + setValid(!hasErrors()); + setChanged(true); + } + }; } + public void addPropertyChangeListener(PropertyChangeListener listener) { + pcs.addPropertyChangeListener(listener); + } + + public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { + pcs.addPropertyChangeListener(propertyName, listener); + } + + public void removePropertyChangeListener(PropertyChangeListener listener) { + pcs.removePropertyChangeListener(listener); + } + + public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { + pcs.removePropertyChangeListener(propertyName, listener); + } + public BeanValidatorErrorListModel getErrorListModel() { return errorListModel; } + public JComponent getFieldRepresentation(String fieldname) { + return fieldRepresentation.get(fieldname); + } + + /** + * Retourne vrai si l'objet bean a ete modifie depuis le dernier + * {@link #setBean} + * + * @return <code>true</code> if bean was modify since last {@link #setBean(Object)} invocation + */ + public boolean isChanged() { + return changed; + } + + public boolean isValid() { + return valid; + } + + public T getBean() { + return bean; + } + + public Class<? extends AbstractBeanValidatorUI> getUiClass() { + return uiClass; + } + + public Map getFieldErrors() { + return validationContext.getFieldErrors(); + } + + public Collection getActionErrors() { + return validationContext.getActionErrors(); + } + + public Collection getActionMessages() { + return validationContext.getActionMessages(); + } + + public Map<String, String> getConversionErrors() { + return conversionErrors; + } + public void setErrorListModel(BeanValidatorErrorListModel errorListModel) { this.errorListModel = errorListModel; if (errorListModel != null) { + // register the validator in the model list errorListModel.registerValidator(this); } } @@ -216,21 +307,7 @@ } } - public JComponent getFieldRepresentation(String fieldname) { - return fieldRepresentation.get(fieldname); - } - /** - * Retourne vrai si l'objet bean a ete modifie depuis le dernier - * {@link #setBean} - * - * @return <code>true</code> if bean was modify since last {@link #setBean(Object)} invocation - */ - public boolean isChanged() { - return changed; - } - - /** * Permet de force la remise a false de l'etat de changement du bean * * @param changed flag to force reset of property {@link #changed} @@ -238,11 +315,15 @@ public void setChanged(boolean changed) { boolean oldChanged = this.changed; this.changed = changed; - pcs.firePropertyChange("changed", oldChanged, changed); + pcs.firePropertyChange(CHANGED_PROERTY, oldChanged, changed); } - public T getBean() { - return bean; + public void setValid(boolean valid) { + boolean oldValid = this.valid; + this.valid = valid; + if (oldValid != valid) { + pcs.firePropertyChange(VALID_PROERTY, oldValid, valid); + } } public void setBean(T bean) { @@ -250,19 +331,19 @@ // clean conversions of previous bean conversionErrors.clear(); - if (this.bean != null) { + if (oldBean != null) { try { Method method = this.bean.getClass().getMethod("removePropertyChangeListener", PropertyChangeListener.class); - method.invoke(this.bean, l); + method.invoke(oldBean, l); } catch (Exception eee) { log.info("Can't register as listener", eee); } } this.bean = bean; - if (this.bean != null) { + if (bean != null) { try { Method method = this.bean.getClass().getMethod("addPropertyChangeListener", PropertyChangeListener.class); - method.invoke(this.bean, l); + method.invoke(bean, l); } catch (Exception eee) { log.info("Can't register as listener", eee); } @@ -273,11 +354,14 @@ errorListModel.addErrors(this); } setValid(!hasErrors()); - pcs.firePropertyChange("bean", oldBean, bean); + setChanged(false); + pcs.firePropertyChange(BEAN_PROERTY, oldBean, bean); } - public Class<? extends AbstractBeanValidatorUI> getUiClass() { - return uiClass; + public void setContextName(String contextName) { + String oldValidationContextName = this.contextName; + this.contextName = contextName; + pcs.firePropertyChange(CONTEXT_NAME_PROPERTY, oldValidationContextName, contextName); } public void setUiClass(Class<? extends AbstractBeanValidatorUI> uiClass) { @@ -291,22 +375,6 @@ //return validationContext.hasFieldErrors() || validationContext.hasActionErrors(); } - public Map getFieldErrors() { - return validationContext.getFieldErrors(); - } - - public Collection getActionErrors() { - return validationContext.getActionErrors(); - } - - public Collection getActionMessages() { - return validationContext.getActionMessages(); - } - - public Map<String, String> getConversionErrors() { - return conversionErrors; - } - /** install ui on required components */ public void installUIs() { SwingUtilities.invokeLater(new Runnable() { @@ -338,13 +406,16 @@ * @return the converted value, or null if conversion was not ok */ public <T> T convert(String fieldName, String value, Class<T> valueClass) { + if (fieldName == null) { + throw new IllegalArgumentException("fieldName can not be null"); + } if (valueClass == null) { throw new IllegalArgumentException("valueClass can not be null"); } // on ne convertit pas si il y a un bean et que le resultat de la validation // pourra etre affiche quelque part - if (bean == null || getErrorListModel() == null || fieldRepresentation.size() == 0 || value == null) { + if (checkState() || value == null) { return null; } @@ -358,11 +429,11 @@ throw new RuntimeException("could not find converter for the type " + valueClass); } result = (T) converter.convert(valueClass, value); - if (result != null && !value.equals(result.toString())) { + /* Why this test ? if (result != null && !value.equals(result.toString())) { conversionErrors.put(fieldName, "error.convertor." + Introspector.decapitalize(valueClass.getSimpleName())); result = null; validate(); - } + }*/ } catch (ConversionException e) { // get conversionErrors.put(fieldName, "error.convertor." + Introspector.decapitalize(valueClass.getSimpleName())); @@ -385,7 +456,7 @@ public void validate() { // on ne valide que si il y a un bean et que le resultat de la validation // pourra etre affiche quelque part - if (bean == null || getErrorListModel() == null || fieldRepresentation.size() == 0) { + if (checkState()) { return; } @@ -393,19 +464,20 @@ validationSupport.clearErrorsAndMessages(); - getValidator().validate(bean, null, validationContext); + getValidator().validate(bean, contextName, validationContext); // add the registred conversion errors transfertConversionErrorsToFieldErrors(); + if (log.isTraceEnabled()) { + log.trace("Action errors: " + validationContext.getActionErrors()); + log.trace("Action messages: " + validationContext.getActionMessages()); + log.trace("Field errors: " + validationContext.getFieldErrors()); + } + if (log.isDebugEnabled()) { - log.debug("Action errors: " + validationContext.getActionErrors()); - log.debug("Action messages: " + validationContext.getActionMessages()); - log.debug("Field errors: " + validationContext.getFieldErrors()); + log.debug(this + " : " + validationContext.getFieldErrors()); } - - log.info(this + " : " + validationContext.getFieldErrors()); - // add errors errorListModel.addErrors(this); @@ -421,6 +493,11 @@ } } + /** @return <code>true</code> if validation is active, <code>false</code> otherwise. */ + protected boolean checkState() { + return bean == null || getErrorListModel() == null || fieldRepresentation.size() == 0; + } + protected ActionValidatorManager getValidator() { if (validator == null) { ConfigurationManager confManager = new ConfigurationManager(); @@ -435,7 +512,7 @@ validator = conf.getContainer().getInstance( ActionValidatorManager.class, "no-annotations"); } - //TC - 20081024 : since context is in a ThreadLocal variable, we must do the check + //TC - 20081024 : since context is in a ThreadLocal variable, we must do the check if (ActionContext.getContext() == null) { ActionContext.setContext(context); } @@ -488,45 +565,4 @@ validationContext.setFieldErrors(map); } - public void addPropertyChangeListener(PropertyChangeListener listener) { - pcs.addPropertyChangeListener(listener); - } - - public void removePropertyChangeListener(PropertyChangeListener listener) { - pcs.removePropertyChangeListener(listener); - } - - public boolean isValid() { - return valid; - } - - public void setValid(boolean valid) { - boolean oldValid = this.valid; - this.valid = valid; - if (oldValid != valid) { - pcs.firePropertyChange(VALID_PROERTY, oldValid, valid); - } - } - - protected class Listener implements PropertyChangeListener { - - public void propertyChange(PropertyChangeEvent evt) { - // only validate if the property has really changed... - Object oldValue = evt.getOldValue(); - Object newValue = evt.getNewValue(); - if (oldValue == null && newValue == null) { - return; - } - if ((oldValue != null && oldValue.equals(newValue)) || (newValue != null && newValue.equals(oldValue))) { - return; - } - if (log.isDebugEnabled()) { - log.debug("launch validation from event [name:" + evt.getPropertyName() + " <old:" + oldValue + ", new:" + newValue + ">]"); - } - validate(); - setValid(!hasErrors()); - setChanged(true); - } - } - } Modified: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/validator/ErrorListMouseListener.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/validator/ErrorListMouseListener.java 2008-11-16 17:42:00 UTC (rev 1016) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/runtime/validator/ErrorListMouseListener.java 2008-11-16 17:44:15 UTC (rev 1017) @@ -31,10 +31,9 @@ return; } JComponent component = entry.getComponent(); - if (component == null) { - return; + if (component != null) { + component.requestFocus(); } - component.requestFocus(); } } @@ -42,7 +41,7 @@ protected BeanValidatorError<?> getSelectedError(MouseEvent e) { JList list = (JList) e.getSource(); if (!(list.getModel() instanceof BeanValidatorErrorListModel)) { - log.warn("model must ne a " + BeanValidatorErrorListModel.class + ", but was " + list.getModel()); + log.warn("model must be a " + BeanValidatorErrorListModel.class + ", but was " + list.getModel()); return null; } Modified: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/DefaultObjectHandler.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/DefaultObjectHandler.java 2008-11-16 17:42:00 UTC (rev 1016) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/DefaultObjectHandler.java 2008-11-16 17:44:15 UTC (rev 1017) @@ -280,7 +280,7 @@ * * @param objectCode Java code which evaluates to the object to which to add the listener * *@param dataBinding the name of the data binding this listener is a part of - * @param dataBinding databinding + * @param dataBinding databinding * @param memberName the name of the field or method to listen to * @param propertyChangeListenerCode Java code which evaluates to a <code>PropertyChangeListener</code> * @param compiler the current <code>JAXXCompiler</code> @@ -317,8 +317,7 @@ propertyName = Introspector.decapitalize(memberName.substring("get".length())); } else if (memberName.startsWith("is")) { propertyName = Introspector.decapitalize(memberName.substring("is".length())); - } - else { + } else { try { getBeanClass().getFieldDescriptor(memberName); propertyName = memberName; @@ -351,12 +350,12 @@ if (eventInfo != null) { // a "proxied" event is one that doesn't fire PropertyChangeEvent, so we need to convert its native event type into PropertyChangeEvents StringBuffer result = new StringBuffer(); String methodName = "$pr" + compiler.getUniqueId(propertyChangeListenerCode); - boolean methodExists = compiler.hasMethod(methodName); + boolean methodExists = compiler.hasMethod(methodName); if (!methodExists) { ClassDescriptor eventClass = getEventClass(eventInfo.listenerClass); compiler.addMethodToJavaFile(new JavaMethod(Modifier.PUBLIC, "void", methodName, new JavaArgument[]{new JavaArgument(JAXXCompiler.getCanonicalName(eventClass), "event")}, null, - propertyChangeListenerCode + ".propertyChange(null);")); + propertyChangeListenerCode + ".propertyChange(null);")); } try { String modelMemberName = eventInfo.modelName != null ? "get" + JAXXCompiler.capitalize(eventInfo.modelName) : null; @@ -464,7 +463,7 @@ String modelName, String addMethod, String removeMethod) { try { addProxyEventInfo(memberName, ClassDescriptorLoader.getClassDescriptor(listenerClass.getName()), modelName, addMethod, removeMethod); - } catch (ClassNotFoundException e) { + } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } @@ -621,6 +620,9 @@ for (Attr attribute : attributes) { String name = attribute.getName(); String value = attribute.getValue(); + if (name.equals("javaBean")) { + continue; + } if (name.equals("constraints") || isEventHandlerName(name)) { compiler.preprocessScript(value); // adds dependencies as a side effect } else if (name.equals("constructorParams")) { @@ -658,14 +660,33 @@ if (name.equals("id") || name.equals("constraints") || name.equals("constructorParams") || name.equals("styleClass") || name.startsWith("xmlns") || JAXXCompiler.JAXX_INTERNAL_NAMESPACE.equals(attribute.getNamespaceURI())) { // ignore, already handled - } else if (isEventHandlerName(name)) { + continue; + } + if (name.equals("javaBean")) { + object.setJavaBean(true); + return; + } + if (name.equals("implements")) { + if (object != compiler.getRootObject()) { + // can ony be apply to root object + compiler.reportError("'implements' attribute can only be found on root tag but was found on tag " + tag); + return; + } + String[] interfaces = value.split(","); + compiler.setExtraInterfaces(interfaces); + return; + } + + if (isEventHandlerName(name)) { + // event handler if (!value.endsWith(";")) { - value+=";"; + value += ";"; } addEventHandler(object, Introspector.decapitalize(name.substring(2)), value, compiler); - } else { - setAttribute(object, name, value, true, compiler); + continue; } + // simple property + setAttribute(object, name, value, true, compiler); } } @@ -840,8 +861,9 @@ TagManager.getTagHandler(objectClass).applyStylesheets(child, compiler, mergedStylesheet, isRoot); object.appendInitializationCode(child.getInitializationCode(compiler)); } - } else if (stylesheet != null) + } else if (stylesheet != null) { stylesheet.applyTo(object, compiler, overrides); + } } catch (ClassNotFoundException e) { throw new CompilerException(e); @@ -879,8 +901,9 @@ } catch (CompilerException e) { compiler.reportError("While parsing event handler for '" + name + "': " + e.getMessage()); } - } else + } else { compiler.reportError("could not find event '" + name + "' for object " + object); + } } @@ -951,7 +974,7 @@ * @throws CompilerException if a compilation error occurs */ public void setProperty(CompiledObject object, String name, Object value, JAXXCompiler compiler) { - object.appendInitializationCode(getSetPropertyCode(object.getJavaCode(), name, TypeManager.getJavaCode(value), compiler)); + object.appendInitializationCode(getSetPropertyCode(object.getJavaCodeForProperty(name), name, TypeManager.getJavaCode(value), compiler)); } Modified: lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/validator/BeanValidatorHandler.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/validator/BeanValidatorHandler.java 2008-11-16 17:42:00 UTC (rev 1016) +++ lutinjaxx/trunk/jaxx-core/src/main/java/jaxx/tags/validator/BeanValidatorHandler.java 2008-11-16 17:44:15 UTC (rev 1017) @@ -42,6 +42,8 @@ public static final String UI_CLASS_ATTRIBUTE = "uiClass"; public static final String STRICT_MODE_ATTRIBUTE = "strictMode"; + public static final String CONTEXT_NAME_ATTRIBUTE = "contextName"; + /** to use log facility, just put in your code: log.info(\"...\"); */ static Log log = LogFactory.getLog(BeanValidatorHandler.class); @@ -88,6 +90,10 @@ error = info.addBean(tag, this, compiler); } + if (!error) { + error = info.addContextName(this, compiler); + } + if (error) { log.warn("error were detected in second compile pass of CompiledObject [" + info + "]"); } @@ -121,7 +127,8 @@ protected Map<String, String> fields; protected String bean; protected String beanClass; - protected String beanInitializer; + protected String contextName; + //protected String beanInitializer; protected String uiClass; protected String errorListModel; protected String errorList; @@ -147,57 +154,66 @@ if (BEAN_ATTRIBUTE.equals(property)) { if (value != null && !value.trim().isEmpty()) { - bean = value; + bean = value.trim(); } return; } + if (CONTEXT_NAME_ATTRIBUTE.equals(property)) { + if (value != null && !value.trim().isEmpty()) { + contextName = value.trim(); + } + return; + } if (BEAN_CLASS_ATTRIBUTE.equals(property)) { if (value != null && !value.trim().isEmpty()) { - beanClass = value; + beanClass = value.trim(); } return; } - if (BEAN_INITIALIZER_ATTRIBUTE.equals(property)) { + /*if (BEAN_INITIALIZER_ATTRIBUTE.equals(property)) { if (value != null && !value.trim().isEmpty()) { beanInitializer = value; } return; - } + }*/ if (ERROR_LIST_MODEL_ATTRIBUTE.equals(property)) { if (value != null && !value.trim().isEmpty()) { - errorListModel = value; + errorListModel = value.trim(); } return; } if (ERROR_LIST_ATTRIBUTE.equals(property)) { if (value != null && !value.trim().isEmpty()) { - errorList = value; + errorList = value.trim(); } return; } if (UI_CLASS_ATTRIBUTE.equals(property)) { if (value != null && !value.trim().isEmpty()) { - uiClass = value; + uiClass = value.trim(); } return; } if (AUTOFIELD_ATTRIBUTE.equals(property)) { if (value != null && !value.trim().isEmpty()) { - autoField = (Boolean) TypeManager.convertFromString(value, Boolean.class); + autoField = (Boolean) TypeManager.convertFromString(value.trim(), Boolean.class); } return; } if (STRICT_MODE_ATTRIBUTE.equals(property)) { if (value != null && !value.trim().isEmpty()) { - strictMode = (Boolean) TypeManager.convertFromString(value, Boolean.class); + strictMode = (Boolean) TypeManager.convertFromString(value.trim(), Boolean.class); } return; } + + throw new CompilerException("property " + property + " is not allowed on object " + this); + //todo should not allowed to find other attributes - super.addProperty(property, value); + //super.addProperty(property, value); } public String getBean() { @@ -224,6 +240,10 @@ return beanClass; } + public String getContextName() { + return contextName; + } + public JAXXBeanInfo getBeanDescriptor(JAXXCompiler compiler) { if (beanDescriptor == null && foundBean()) { @@ -290,7 +310,7 @@ } else { if (errorListModel.startsWith("{") && errorListModel.endsWith("}")) { // this is a script, no check here - errorListModel = errorListModel.substring(1,errorListModel.length()-1).trim(); + errorListModel = errorListModel.substring(1, errorListModel.length() - 1).trim(); } else if (!compiler.checkReference(tag, errorListModel, true, ERROR_LIST_MODEL_ATTRIBUTE)) { // errorListModel is not defined return true; @@ -304,6 +324,14 @@ } + protected boolean addContextName(BeanValidatorHandler handler, JAXXCompiler compiler) { + if (contextName != null) { + String code = handler.getSetPropertyCode(getJavaCode(), CONTEXT_NAME_ATTRIBUTE, TypeManager.getJavaCode(contextName), compiler); + appendAdditionCode(code); + } + return false; + } + protected boolean addErrorList(Element tag, JAXXCompiler compiler) { if (errorList == null) { @@ -332,22 +360,32 @@ return true; } + String beanInitializer = null; if (bean != null) { - if (!compiler.checkReference(tag, bean, true, BEAN_ATTRIBUTE)) { - // could not find bean in compiled object - return true; - } + if (bean.startsWith("{") && bean.endsWith("}")) { - if (compiler.isBeanUsedByValidator(bean)) { - compiler.reportError("the bean '" + bean + "' is already used in another the validator, can not used it in '" + tag + "'"); - return true; - } + // just has an intializer + beanInitializer = bean.substring(1, bean.length() - 1); + // this is not a real bean, so delete it + bean=null; + } else { - if (beanInitializer != null) { - compiler.reportWarning("tag '" + tag + "' found a 'bean' and a 'beanInitializer' attributes, 'beanInitializer' is skipped"); + if (!compiler.checkReference(tag, bean, true, BEAN_ATTRIBUTE)) { + // could not find bean in compiled object + return true; + } + + if (compiler.isBeanUsedByValidator(bean)) { + compiler.reportError("the bean '" + bean + "' is already used in another the validator, can not used it in '" + tag + "'"); + return true; + } + + /*if (beanInitializer != null) { + compiler.reportWarning("tag '" + tag + "' found a 'bean' and a 'beanInitializer' attributes, 'beanInitializer' is skipped"); + }*/ + beanInitializer = bean; } - beanInitializer = bean; } if (beanInitializer != null) { @@ -357,10 +395,10 @@ // add generic type to validator JAXXBeanInfo beanInfo = getBeanDescriptor(compiler); - if (beanInfo==null) { + if (beanInfo == null) { return true; } - + setGenericTypes(new String[]{beanInfo.getJAXXBeanDescriptor().getClassDescriptor().getName()}); if (getAutoField()) { Added: lutinjaxx/trunk/jaxx-core/src/site/fr/rst/BeanValidator.rst =================================================================== --- lutinjaxx/trunk/jaxx-core/src/site/fr/rst/BeanValidator.rst (rev 0) +++ lutinjaxx/trunk/jaxx-core/src/site/fr/rst/BeanValidator.rst 2008-11-16 17:44:15 UTC (rev 1017) @@ -0,0 +1,179 @@ +------------- +BeanValidator +------------- + +.. contents:: + + +Présentation +============ + +Ajout du support de validation dans JAXX. + +La techonologie utilisée est celle de Struts 2 (XWorks 2). + + +Configuration +============= + +La configuration des validateurs se font via des fichier xml (on peut aussi utiliser des annotations,...). + +Ajout d'un validateur +********************* + +Pour enregister un nouveau validateur sur un bean, il suffit de placer dans le même paquetage que le bean un fichier *XXX-validation.xml* où XXX est le nom non qualifié du bean. + +On peut de plus affecter un context de validation, dans ce cas le fichier doit s'appeler *XXX-YYY-validation.xml*, où YYY est le nom du context de validation. + +Ainsi on peut valider de différentes manières un bean (par exemple selon son cycle de vie :création, modification, ...). + +Ajout d'un nouveau type de validateur +************************************* + +Il est aussi possible de définir de nouveau type de validateurs : + + * créer une classe qui étend FieldValidator + * ajouter un fichier validators.xml (ou ajouter dans un tel fichier la définition du nouveau validator) à la racine du class-path. + +I18n +**** + +Afin de rendre le mécanisme multi-langue, on propose dans les fichiers de validations d'utiliser des clef i18n pour les messages. + +Un nouveau parseur dans notre plugin i18n a été ajouté pour détecter ces clefs. (*maven-i18n-plugin:0.7:parserValidation*) + + +Intégration dans JAXX +===================== + +Deux nouveaux tags ont été conçus pour pouvoir décrire un validateur dans les fichiers JAXX. + +Ce développement est dans le paquetage *jaxx.tags.validator*. + +tag BeanValidator +***************** + +Permet de définir un nouveau validateur dans une classe JAXX. + +Les attributs autorisés sont les suivants : + + * *id* : le nom du validateur + + * *bean* : l'id d'un bean connu par la classe JAXX. On ne peut pas utiliser ici une expression car on n'est pas sûr de pouvoir déterminer le type de cette expression pendant le parsing ? (a verifier). + + * *beanClass* : le FQN de classe du bean à valider. Peut-être non présent si l'attribut *bean* est renseigné. + + * *beanInitializer* : une expression pour initialiser le bean à valider. TODO a revoir : on devrait utiliser uniquement un seul attribut *bean*... + + * *contextName* : le nom du contexte de validation. + + * *autoField* : flag pour indiquer l'inscription implicite des validateurs de champs du bean. Pour ce faire on parcourt l'ensemble des champs du bean et on ne considère uniquement ceux dont on connait dans la classe JAXX un composent d'édition du champ (id=nom du champ). Il est possible de surcharger ce configuration implicite en rajoutant explicitement des champs (voir le tag *field*). + + * *errorList* : le composant graphique pour afficher la liste des erreurs, doit étendre *javax.swing.JList*. Si non présent, on essayera le component d'id *errorList*. + + * *errorListModel* : le modèle qui contient la liste des erreurs (et est liée au composant *errorList*), doit étendre *jaxx.runtime.validator.BeanValidatorErrorListModel*. Si non présent on essayera le composent d'id *errorListModel*. + + * *uiClass* : le FQN de la classe utilisé pour le rendu des erreurs sur les wigets d'édition. La classe doit étendre *jaxx.runtime.validator.ui.AbstractBeanValidatorUI*. Si non présent, on utilise par défaut le render *jaxx.runtime.validator.ui.IconValidationUI*. + + +Le tag supporte aussi l'ajout de tag *field* comme fils pour définir explicitement des champs à validater. + +tag field +********* + +Le tag *field* définit une entrée dans le validateur, on définit une correspondance entre le champ à valider et le composant d'édition de ce champ. + +Le tag supporte les attributs suivants : + + * *name* : le nom de la propriété du bean associée. + Cet attribut est obligatoire. + + * *component* : l'id du composant graphique d'édition associé à la propriété du bean. + Cet attribut peut être omis si le nom de la propriété du bean est identique à celui du composent graphique d'édition. + + +Les classes du runtime +====================== + +Ce développement est dans le paquetage *jaxx.runtime.validator* (sauf pour l'interface *jaxx.runtime.JAXXValidator*). + +Il s'agit de l'ensemble des classes ajoutées dans le module *jaxx-core* pour encapsuler la validation dans les fichiers java générés à partir des fichiers JAXX. + + +interface jaxx.runtime.JAXXValidator +************************************ + +Ce contrat a été ajouté à tous les objets JAXX générés (donc l'interface JAXXObject étend JAXXValidator). + +On définit ici uniquement des méthodes d'accès aux validateurs enregistrés dans le JAXXObject. + +TODO on pourrait ajouter des méthodes pour savoir l'état de validation d'un validateur ? + + +classe jaxx.runtime.validator.BeanValidator +******************************************* + +Il s'agit de la classe principale d'encapsulation d'un validateur XWorks 2. + +Le principe est simple : le validateur écoute les modifications sur le bean associé et revalide le bean à chaque modification. La validation met à jour la liste des erreurs asociées. + +On se base sur les PropertyChangeListener pour écouter les modification des beans. Il faut donc que les beans supporte ces listeners. + +Pour les entités de ToPIA, il suffit de positionner un contexte (topia) au bean pour profiter des listeners liés. + +Pour les DTO de ToPIA, les générateurs ont été modifiés pour gérer ce support. + +Les méthodes à retenir sont : + + * *setBean* pour affecter un bean au validateur (pour déasactiver passer null). Lors de la désactivation d'un bean, les erreurs associés sont retirées de la liste des erreurs. + + * *setContextName* pour affecter un nouveau nom de context de validation (par défaut on utilise pas de context de validation). + + * *validate* pour lancer une validation sur le bean liée (ne sera opérant uniquement si un bean est liée et qu'une liste d'erreur est liée). + + * *isValid* pour connaitre l'état du validateur à un moment donné. + +Normalement la méthode *validate* ne devrait pas être appelée directement. : elle est automatiquement invoquée lorsqu'une propriété du bean est modifiée. + +classe jaxx.runtime.validator.BeanValidatorError +************************************************ + +Modélisation d'une erreur renvoyé par le validateur, on conserve ici : + + * le validateur qui a provoqué l'erreur + + * la propriété du bean en faute + + * le composent graphique d'édtion de la propriété + +classe jaxx.runtime.validator.BeanValidatorErrorListModel +********************************************************* + +Le modèle de la liste des erreurs renvoyées par le validateur. Il s'agit d'une extension d'un *javax.swing.DefaultListModel* qui permet de gérer une liste d'erreurs provenant de plusieurs validateurs en même temps. + +classe jaxx.runtime.validator.ErrorListMouseListener +**************************************************** + +Un listener écoutant les double clics sur une liste d'erreurs et qui donne le focus au composent graphique d'édition associée à la propriété dont l'erreur est sélectionné. + + +Les conversions +=============== + +Pour l'édition de proriétés qui ne sont pas des chaines de caractères, des erreurs de conversions peuvent survenir (conversion de la valeur de l'édtieur graphique vers le bean), avant que l'on utilise +réellement la mécanique de la validation. + +Pour palier à ce problème on a intégré la gestion des erreurs de conversion dans le validateur. +Pour ce faire, il suffit de faire appel à la méthode suivante pour injecter dans un bean une propriété : + + :: + + jaxx.runtime.Util.convert(validator,"nomDeLaPropriété",widget.getText(),TypeDeLaProriete.class); + + +On obtiendra si une erreur de conversion intervient, une erreur dont le libellé est de la forme *error.convertor.XXX* +où XXX est nom simple en minuscule du type de la propruité. (par exemple : *error.convertor.integer*). + + + + Copied: lutinjaxx/trunk/jaxx-core/src/site/fr/rst/I18n.rst (from rev 1003, lutinjaxx/trunk/jaxx-core/src/site/fr/rst/index.rst) =================================================================== --- lutinjaxx/trunk/jaxx-core/src/site/fr/rst/I18n.rst (rev 0) +++ lutinjaxx/trunk/jaxx-core/src/site/fr/rst/I18n.rst 2008-11-16 17:44:15 UTC (rev 1017) @@ -0,0 +1,56 @@ +---- +I18n +---- + +.. contents:: + + +Présentation +============ + +Ajout du support i18n dans JAXX. + +On utilise la système i18n développé dans la librairie des lutins (*lutinlib*). + +Configuration +============= + +Une option a été ajoutée dans le plugin maven de JAXX : *i18nable* pour permettre ou non l'utilisation de ce système. + +Par défaut, l'option est active (donc aucune configuration supplémentaire n'est nécessaire pour utiliser i18n dans JAXX). + + +Fonctionnement +============== + +Le fonctionnement est simple : lors de la compilation JAXX, lorsque l'on rencontre certains attributs on encapsule la +valeur de l'attribution par un appel à la méthode + +:: + + org.codelutin.i18n.I18n._(String) + + +La liste des attributs en question sont les suivants : + + - title + - text + - toolTipText + + +Ensuite l'utilisation du *parserJava* du plugin i18n permet la détection des clefs i18n. + + +Pourquoi ne plus utiliser l'ancien parserJaxx du plugin i18n +============================================================ + +Un parseur avait été développé initialement pour détecter dans les fichiers JAXX (*parserJaxx*), les appels à la méthode de traduction +i18n dans les attributs. + +Ce parseur n'est plus d'actualité car on ne doit plus avoir d'appel explicite à cette méthode dans les fichiers JAXX. + +De plus, même sans avoir mis en place le mécanisme i18n dans JAXX, ce parseur n'était plus d'actualité : il se base +sur une liste prédéfinie d'attributs xml à scanner pour détecter des appels i18n; ce qui n'était pas acceptable à terme +car si on ajoute un nouveau composent dans JAXX, le plugin i18n n'est pas capable de détecter les nouvelles clefs. + +Donc on préconise l'utilisation du *parserJava* du plugin i18n qui lui est capable de détecter tous les appels i18n. Property changes on: lutinjaxx/trunk/jaxx-core/src/site/fr/rst/I18n.rst ___________________________________________________________________ Name: svn:mergeinfo + Added: lutinjaxx/trunk/jaxx-core/src/site/fr/rst/Interface.rst =================================================================== --- lutinjaxx/trunk/jaxx-core/src/site/fr/rst/Interface.rst (rev 0) +++ lutinjaxx/trunk/jaxx-core/src/site/fr/rst/Interface.rst 2008-11-16 17:44:15 UTC (rev 1017) @@ -0,0 +1,38 @@ +--------- +Interface +--------- + +.. contents:: + + +Présentation +============ + +Ajout de contrats sur le code généré dans JAXX. + +Mécanisme +========= + +Le compilateur JAXX génère des classes à partir de fichiers JAXX mais n'est pas capable d'ajouter des contrats sur +les objets générés, donc interdit en quelque sorte la programmation par contrat. + +Pour palier à cette limitation, on a ajouté un attribut spécial *implements*. + +Cette attribut ne doit être placé que sur le tag racine d'un fichier JAXX et son contenu est le nom qualifié d'un ou +plusieurs contrats séparaés par des virgules. + +:: + + <JPanel implements='java.lang.Comparable'> + + <script>public int compareTo(JPanel o) { return getName().compareTo(o.getName()); }</script> + + </JPanel> + +La classe générée aura bien le contrat *java.lang.Comparable*. + +TODO +==== + +Il serait intéressant lors de l'injection de contrats sur un objet jaxx de pouvoir vérifier si toutes les méthodes du +contrat sont bien implantées dans la classe, et si ce n'est pas le cas de rendre la classe abstraite. Added: lutinjaxx/trunk/jaxx-core/src/site/fr/rst/JAXXContext.rst =================================================================== --- lutinjaxx/trunk/jaxx-core/src/site/fr/rst/JAXXContext.rst (rev 0) +++ lutinjaxx/trunk/jaxx-core/src/site/fr/rst/JAXXContext.rst 2008-11-16 17:44:15 UTC (rev 1017) @@ -0,0 +1,161 @@ +----------- +JAXXContext +----------- + +.. contents:: + + +Présentation +============ + +Ajout d'un context applicatif dans JAXX. + +Le besoin initial de ce développement est de pouvoir facilement intégrer un context applicatif dans JAXX et de pouvoir +l'utiliser dans les fichiers JAXX pour injecter par exemple des données dans les widgets. + +jaxx.runtime.JAXXContext +======================== + +Il s'agit du contrat de base du context applicatif. + +Les entrées dans le context +*************************** + +Chaque donnée utilisable dans le context est caractérisée par deux propriétés : + + * la type de l'objet + + * un nom (facultatif) associé à la donnée + +Le type de l'objet correspondant en fait à la classe de la donnée. + +Le nom qui est facultatif permet de pouvoir distinguer plusieurs données d'un même type dans le context. Si le nom +n'est pas utilisé pour caractériser une données on fixera alors sa valeur à *null*. + +Afin de pouvoir caractériser les entrées dans le context, une classe a été définie *jaxx.runtime.JAXXContextEntryDef*. + +Les méthodes de lecture +*********************** + +On a définit deux méthodes de lecture de données dans le context : + + * *getContextValue(Class)* récupère la donnée *non nommée* dont le type correspond à celui passé. + + * *getContextValue(Class,String)* récupère la donnée nommé dont le type correspond à celui passé. + +Les méthodes d'écriture +*********************** + +On a définit quatre méthodes d'écriture de données dans le context : + + * *setContextValue(Object)* enregistre dans le context la donnée *non nommée*. + + * *setContextValue(Object,String)* enregistre dans le context la donnée *nommée*. + + * *removeContextValue(Class)* supprime du context, la donnée *non nommée* dont le type est passé. + + * *removeContextValue(Class, String)* supprime du context, la donnée *nommée* dont le type est passé. + +Afin de pouvoir assurer une cohérence dans le context, chaque injection de donnée, sera toujours précédée par une +suppression d'un éventuellement ancienne valeur qui aurait la même définition d'entrée. + +L'héritage +********** + +Il est possible d'avoir une hériarchie de context qui s'enchaînent. Ce mécanisme a été mis en place pour répondre à un +besion précis : chaque fichier JAXX donne lieu à une nouvelle classe qui possède son propre context. + +On peut avoir des fichiers JAXX qui utilisent d'autres fichiers JAXX, il faut donc être capable de lier le context du +composent parent avec ses fils. + +La mise en place de l'héritage est transparente pour l'utilisateur : aucune méthode supplémentaire n'est requise. + +Les implantations de context +============================ + +jaxx.runtime.DefaultJAXXContext +******************************* + +Il s'agit de l'implantation par défaut utilisée par les objects *JAXXObject* (objets générés). + +A noter que pour les opérations d'injection ou de suppression, on effectuera les opérations sur le context qui contient +réellement la donnée, ce qui est important pour conserver la cohérence des contexts dans les contexts chaînés. + +Pour traiter le context parent, aucune méthode publique supplémentaire n'a été rajoutée, il suffit d'injecter un objet +de type *JAXXContext* *non nommé* qui le context courant qui sera détecté comme une entrée de type context. + +Cette entrée spéciale ne sera pas stockée avec les autres entrées afin d'optimiser les algorithmes d'injection et de +restitution. + +jaxx.runtime.JAXXInitialContext +******************************* + +On a implanté un second type de context qui lui peut servir à l'initialisation des JAXXObject. + +Ce second type de context ajoute des méthodes pour préparer le context avant l'instanciation des JAXXObject. + +Ce context admet certaine limitation (pas de suppression dans le context), et apporte trois nouvelles méthodes : + + * add(Object) : injecte dans le context une entrée non nommée et retourne l'instance du context. + + * add(String,Object) : injecte dans le context une entrée nommé et retourne l'instance du context. + + * to(JAXXContext) : injecte dans le context passé toutes les entrée du context. + +Les méthodes *add* peuvent être chaînées comme dans l'exemple suivant : + +:: + + JAXXInitialContext context = new JAXXInitialContext().add("string").add(0).add("currentDate",new Date()); + + +Intégration dans les JAXXObject +=============================== + +Le traitement d'un fichier JAXX donne lieu à une classe qui possède le contrat *jaxx.runtime.JAXXObject*. + +Ce contrat hérite donc du contrat *JAXXContext* (afin de pouvoir l'utiliser de manière transparente dans les fichiers JAXX). + +Afin de simplifier la génération et les évolutions du context indépendemment des évolutions des JAXXObject, on utilise +un pattern de délégation au sein des object générés. + +Paramétrage de l'implantation du context +**************************************** + +Une propriété a été rajoutée sur le plugin JAXX, afin de pouvoir préciser l'implantation de context à utiliser : + +:: + + jaxx.jaxxContextImplementorClass + +Il s'agit du nom qualifié de la classe d'implantation à utiliser. + +Par défaut, si rien n'est renseigné, on utilisera un *jaxx.runtime.DefaultJAXXContext*. + + +Initialisation d'un JAXXObject +****************************** + +Un nouveau constructeur a été ajouté dans les JAXXObjet générés afin de pouvoir facilement initialisé un tel objet à +partir d'un context parent, son unique paramètre est le context parent. Ce constructeur est capable de différencer le +type de context passé : + + * s'il s'agit d'un *JAXXInitialContext*, on recopie alors dans le context réel de l'objet toutes les entrées du context passé. + + * sinon le context passé est simplement injecté dans le context de l'objet (et donc sera utilisé comme le context parent de celui de l'objet). + +Voici un exemple d'initialisation d'un JAXXObject : + +:: + + java.util.Date currentDate = new java.util.Date(); + JAXXInitialContext context = new JAXXInitialContext().add("string").add(0).add("currentDate",currentDate); + JAXXObject ui = new MyUI(context); + + assert "string".equals(myUI.getContextValue(String.class)); + assert 0 == myUI.getContextValue(Integer.class); + assert currentDate.equals(myUI.getContextValue(java.util.Date.class,"currentDate")); + +A noter, que l'initialisation du context d'un *JAXXObject* sera toujours effectuée avant la méthode *$initialize* générée +par le compilateur JAXX, ce qui permet de pouvoir utiliser le context pour l'initialisation des widgets dans les fichiers JAXX. + Added: lutinjaxx/trunk/jaxx-core/src/site/fr/rst/JavaBean.rst =================================================================== --- lutinjaxx/trunk/jaxx-core/src/site/fr/rst/JavaBean.rst (rev 0) +++ lutinjaxx/trunk/jaxx-core/src/site/fr/rst/JavaBean.rst 2008-11-16 17:44:15 UTC (rev 1017) @@ -0,0 +1,50 @@ +-------- +JavaBean +-------- + +.. contents:: + + +Présentation +============ + +Ajout du support complêt des javaBean dans JAXX. + +Mécanisme +========= + +Il est possible dans JAXX de rajouter des objets quelconques via leur nom qualifié de classe : + +:: + + <JPanel> + <java.lang.Boolean id='myState' constructorParams='true'/> + <JLabel text='text' visible='{isMySate()}'/> + </JPanel> + +Avant l'ajout de la fonctionnalité, le code généré possèdait : + + * une propriété en lecture seul nommé *myState*. + +Aucun support javaBean n'était présent et le databinding sur la propriété *visible* du label n'est pas créé. Cela veut +dire que le label sera initialisé avec la valeur initiale du boolean et c'est tout... + +Avec l'ajout du support javaBean, on peut maintenant faire ces bindings, pour ce faire il suffit d'ajouter un attribut +*javaBean* sur l'objet : + +:: + + <JPanel> + <java.lang.Boolean id='myState' constructorParams='true' javaBean='anyValue'/> + <JLabel text='text' visible='{isMySate()}'/> + </JPanel> + +On aura donc en plus : + + * un mutateur sur la propriété *myState* qui déclanchera l'envoie d'un *PropertyChange* sur la propriété lors de modification de valeur. + +Ainsi le compilateur JAXX sera capable d'enregistrer un novueau dataBindig sur la propriété *visible* du label et la +modification de l'état *myState* sera automatiquement répercuté sur la propriété. + + + Added: lutinjaxx/trunk/jaxx-core/src/site/fr/rst/NavigationTreeModel.rst =================================================================== --- lutinjaxx/trunk/jaxx-core/src/site/fr/rst/NavigationTreeModel.rst (rev 0) +++ lutinjaxx/trunk/jaxx-core/src/site/fr/rst/NavigationTreeModel.rst 2008-11-16 17:44:15 UTC (rev 1017) @@ -0,0 +1,166 @@ +------------------- +NavigationTreeModel +------------------- + +.. contents:: + + +Présentation +============ + +Ajout d'un modèle d'arbre de navigation. + +Le but de cette fonctionnalité est de pouvoir créer un arbre de navigation lié au context de JAXX, de définir des UI +rattachés à chaque noeud. + +Le développement est effectué dans le paquetage *jaxx.runtime.swing*. + +jaxx.runtime.swing.NavigationTreeModel +====================================== + +Il s'agit du modèle de l'arbre utilisé, c'est une extension d'un *javax.swing.tree.DefaultTreeModel*. + +Les noeuds présents dans ce modèle sont aussi typés en *jaxx.runtime.swing.NavigationTreeModel.NavigationTreeNode*. + +L'idée principale est de pouvoir associé à un noeud précis un chemin depuis la racine, ce que l'on appele *chemin de navigation*. + +Pour obtenir le chemin de navigation d'un noeud donné, on récupère l'enmseble des neoud depuis la racine vers ce noeud et les concatène en suffixant par un *.*. + +Définition d'un noeud +===================== + +Le noeud (*jaxx.runtime.swing.NavigationTreeModel.NavigationTreeNode*) est une extension d'un *javax.swing.tree.DefaultMutableTreeNode*. + +Il apporte les nouvelles propriétés suivantes : + + * *context* : nom de chemin de navigation de ce noeud. + + * *jaxxClass* : nom qualifié de la classe d'ui associé à ce noeud (doit être obligatoirement un *JAXXObject*) + + * *jaxxActionClass* : nom qualifié de la classe d'handler d'ui associé à ce noeud (doit être obligatoirement un *JAXXAction*). + + * *jaxxContextEntryDef* : définition de l'entrée dans le context JAXX associé à ce noeud. + + +Retrouver un noeud à partir du chemin de navigation +*************************************************** + +Il est possible en connaissant le chemin de navigation d'un noued de récupérer le noeud dans l'arbre via la méthode + +:: + + model.findNode("chemin.de.navigation") + +Trouver la valeur associée dans le context JAXX à partir du chemin de navigation +******************************************************************************** + +Il est possible en connaissant le chemin de navigation d'un noued de récupérer la valeur associée dans le context JAXX. + +:: + + model.getJAXXContextValue(myJAXXContext, "chemin.de.navigation")* . + +L'algrotihme est le suivant : + + * récupération du noeud associé au chemin de navigation + + * recherche du premier noeud (dans les noeuds qui remontent vers la racine de l'arbre) dont la propriété *jaxxContextEntry* est non nulle, il s'agit du point d'entré dans le context JAXX. + + * redescendre à partir de ce noeud vers le noeud d'origine (si ce noeud était différent) et en descendant conjointement dans l'objet du context JAXX : + + - le chemin de navigation de chaque noeud rencontré est le nom de la propriété à extraire de l'objet du context JAXX + + - si l'objet courant extrait du context JAXX est une liste, on utilise alors la position dans la liste de l'objet recherché + +Exemple : + +:: + + "$root" + <-- la racine de l'abre + | + + "string" <-- attaché au context JAXX <java.lang.String,"string"> + | + + "liste" + <-- attaché au context JAXX <java.util.List.class,"liste"> + | | + | + "0" <-- le premier élément de la liste + | | + | + "1" <-- le second élément de la liste + | | + | + "2" <-- le troisième élément de la liste + | + + "locale" + <-- attaché au context JAXX <java.util.Locale> + | + + country + | + + language + + // preparation du context + java.util.List myList = java.util.Arrays.asList("one","two","three"); + java.util.Locale myLocale = java.util.Locale.FRENCH; + context.setContextValue(myList, "liste"); + context.setContextValue("stringValue","string"); + context.setContextValue(myLocale); + + + // récupération des valeurs dans le context à partir de chemin de navigation + assert model.getJAXXContextValue(context, "$root") == null; + assert model.getJAXXContextValue(context, "$root.string") == "stringValue"; + assert model.getJAXXContextValue(context, "$root.liste") == myList; + assert model.getJAXXContextValue(context, "$root.liste.0") == "one"; + assert model.getJAXXContextValue(context, "$root.liste.1") == "two"; + assert model.getJAXXContextValue(context, "$root.liste.2") == "three"; + assert model.getJAXXContextValue(context, "$root.locale") == myLocale; + assert model.getJAXXContextValue(context, "$root.locale.country") == myLocale.getCountry(); + assert model.getJAXXContextValue(context, "$root.locale.language") == myLocale.getLanguage(); + + +A noter qu'une seconde méthode de récupération de valeur du context JAXX est disponible pour pouvoir récupérer cette +valeur en connaissant le noeud : + +:: + + model.getJAXXContextValue(context, myNode); + +jaxx.runtime.swing.NavigationTreeSelectionAdapter +================================================= + +Il s'agit d'un listener sur la sélection d'un noeud dans l'arbre de navigation basé sur notre modèle de navigation. Il +étend *javax.swing.event.TreeSelectionListener*. + +Ce listener contient la gestion de passage d'un noeud à un autre avec interaction avec le context JAXX et affichage automatique de l'ui associé au noeud sélectionné. + +**Attention : la mécanique ne fonctionne que pour un arbre à selection unique.** + +Algorithme +********** + +Voici l'algorithme utilisé lors de la sélection d'un nouveau noeud : + + * *closeUI* : tentative de fermeture de l'ui lié au noeud précédemment selectionné, si cela n'aboutit pas on retourne sur le noeud précédent. + + * *attachBeanFromNodeToContext* : injection dans le context JAXX de la valeur associée au nouveau noeud + + * *createUI* : création de la nouvelle ui (si elle n'existe pas) + + * *openUI* : ouverture de la nouvelle ui + +Si une erreur survient lors de ces opérations, on entre dans la méthode *goBackToPreviousNode* qui est abstraite et permet de notifier les erreur de retourner au noeud précdent. + +jaxx.runtime.swing.NavigationtreeSelectionAdapterWithCardLayout +*************************************************************** + +Il s'agit d'une implantation du listener précédent qui suppose que les uis associées aux noeuds sont affichées dans un +unique container en utilisant le layout *jaxx.runtime.swing.CardLayout2*. + +La contrainte de chaque ui sera extactement le chemin de navigation du noeud associé. + +Il possède deux méthodes abstraites : + + * *getContentContainer* : qui indique le container d'ui. + + * *getContentLayout* : qui indique le layout utilisé. + +Pour pouvoir utilisé cet *adapter*, il vous suffit de définir la méthode suivante (en plus des deux méthodes abstraites) : + + * *goBackToPreviousNode* : comment gérer les erreurs et retourner au noeud précedent. + Modified: lutinjaxx/trunk/jaxx-core/src/site/fr/rst/Todo.rst =================================================================== --- lutinjaxx/trunk/jaxx-core/src/site/fr/rst/Todo.rst 2008-11-16 17:42:00 UTC (rev 1016) +++ lutinjaxx/trunk/jaxx-core/src/site/fr/rst/Todo.rst 2008-11-16 17:44:15 UTC (rev 1017) @@ -1,4 +1,15 @@ +==== TODO ==== -a faire \ No newline at end of file + - réorganiser ce module maven en deux modules : + + * un premier module de runtime + + * un second module contenant uniquement le compilateur et non nécessaire au runtime. + + Le second module pourrait être facultatif et le code pourrait directement être placé dans le module du plugin. + + Cependant cela n'est pas possible actuelement car certains objets du runtime contiennent aussi du code utilisé + par le compilateur JAXX (par exemple StyleSheet). Il faut avant tout cloisonner le code non runtime. + \ No newline at end of file Modified: lutinjaxx/trunk/jaxx-core/src/site/fr/rst/index.rst =================================================================== --- lutinjaxx/trunk/jaxx-core/src/site/fr/rst/index.rst 2008-11-16 17:42:00 UTC (rev 1016) +++ lutinjaxx/trunk/jaxx-core/src/site/fr/rst/index.rst 2008-11-16 17:44:15 UTC (rev 1017) @@ -11,3 +11,31 @@ **Veuillez consulter la JavaDoc pour de plus ample détails sur les différentes librairies.** + +Nouvelles fonctionnalités +------------------------- + + * I18n_ + + * JAXXContext_ + + * BeanValidator_ + + * NavigationTreeModel_ + + * JavaBean_ + + * Interface_ + + +.. _I18n: I18n.html + +.. _JAXXContext: JAXXContext.html + +.. _BeanValidator: BeanValidator.html + +.. _NavigationTreeModel: NavigationTreeModel.html + +.. _Javabean: JavaBean.html + +.. _Interface: Interface.html \ No newline at end of file Modified: lutinjaxx/trunk/jaxx-core/src/test/java/jaxx/runtime/DefaultJAXXContextTest.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/test/java/jaxx/runtime/DefaultJAXXContextTest.java 2008-11-16 17:42:00 UTC (rev 1016) +++ lutinjaxx/trunk/jaxx-core/src/test/java/jaxx/runtime/DefaultJAXXContextTest.java 2008-11-16 17:44:15 UTC (rev 1017) @@ -12,7 +12,7 @@ /** @author chemit */ public class DefaultJAXXContextTest { - JAXXContext ctxt; + DefaultJAXXContext ctxt; @Before public void initContext() throws Exception { @@ -36,7 +36,7 @@ public void testParentContainerFail_IllegalArgumentException() throws Exception { // attach a fake ui (which is NOT a Container) - ((DefaultJAXXContext) ctxt).setUi(new MyJAXXObject()); + ctxt.setUi(new MyJAXXObject()); ctxt.getParentContainer(Container.class); } @@ -44,7 +44,7 @@ public void testParentContainerFail_IllegalArgumentException2() throws Exception { // attach a fake ui (which is NOT a Container) - ((DefaultJAXXContext) ctxt).setUi(new MyJAXXObject()); + ctxt.setUi(new MyJAXXObject()); ctxt.getParentContainer(null, Container.class); } @@ -52,7 +52,7 @@ public void testParentContainerFail_IllegalArgumentException3() throws Exception { // attach a fake ui (which is NOT a Container) - ((DefaultJAXXContext) ctxt).setUi(new MyJAXXObject()); + ctxt.setUi(new MyJAXXObject()); ctxt.getParentContainer("null", Container.class); } @@ -148,7 +148,7 @@ expected = "yo"; ctxt.setContextValue(expected, "yo"); - + result = ctxt.getContextValue(String.class); Assert.assertNull(result); result = ctxt.getContextValue(String.class, "yo"); @@ -192,6 +192,85 @@ Assert.assertEquals(expected, result); } + @Test + public void testEntrySet() throws Exception { + Object o = new Object(); + + ctxt.setContextValue(o); + + Assert.assertEquals(1, ctxt.data.size()); + Assert.assertEquals(o, ctxt.getContextValue(Object.class)); + Assert.assertEquals(null, ctxt.getContextValue(Object.class, "named")); + + ctxt.setContextValue(o, "named"); + + Assert.assertEquals(2, ctxt.data.size()); + Assert.assertEquals(o, ctxt.getContextValue(Object.class)); + Assert.assertEquals(o, ctxt.getContextValue(Object.class, "named")); + + ctxt.removeContextValue(Object.class); + Assert.assertEquals(1, ctxt.data.size()); + Assert.assertEquals(null, ctxt.getContextValue(Object.class)); + Assert.assertEquals(o, ctxt.getContextValue(Object.class, "named")); + + ctxt.removeContextValue(Object.class); + Assert.assertEquals(1, ctxt.data.size()); + Assert.assertEquals(null, ctxt.getContextValue(Object.class)); + Assert.assertEquals(o, ctxt.getContextValue(Object.class, "named")); + + ctxt.removeContextValue(Object.class, "named"); + Assert.assertEquals(0, ctxt.data.size()); + Assert.assertEquals(null, ctxt.getContextValue(Object.class)); + Assert.assertEquals(null, ctxt.getContextValue(Object.class, "named")); + + } + + @Test + public void testEntrySetWithParent() throws Exception { + + DefaultJAXXContext parentContext = new DefaultJAXXContext(); + + ctxt.setContextValue(parentContext); + + class Object2 { + + } + Object o = new Object2(); + + parentContext.setContextValue(o); + + Assert.assertEquals(0, ctxt.data.size()); + Assert.assertEquals(1, parentContext.data.size()); + Assert.assertEquals(o, ctxt.getContextValue(Object2.class)); + Assert.assertEquals(null, ctxt.getContextValue(Object2.class, "named")); + + parentContext.setContextValue(o, "named"); + + Assert.assertEquals(0, ctxt.data.size()); + Assert.assertEquals(2, parentContext.data.size()); + Assert.assertEquals(o, ctxt.getContextValue(Object2.class)); + Assert.assertEquals(o, ctxt.getContextValue(Object2.class, "named")); + + parentContext.removeContextValue(Object2.class); + Assert.assertEquals(0, ctxt.data.size()); + Assert.assertEquals(1, parentContext.data.size()); + Assert.assertEquals(null, ctxt.getContextValue(Object2.class)); + Assert.assertEquals(o, ctxt.getContextValue(Object2.class, "named")); + + parentContext.removeContextValue(Object2.class); + Assert.assertEquals(0, ctxt.data.size()); + Assert.assertEquals(1, parentContext.data.size()); + Assert.assertEquals(null, ctxt.getContextValue(Object2.class)); + Assert.assertEquals(o, ctxt.getContextValue(Object2.class, "named")); + + ctxt.removeContextValue(Object2.class, "named"); + Assert.assertEquals(0, ctxt.data.size()); + Assert.assertEquals(0, parentContext.data.size()); + Assert.assertEquals(null, ctxt.getContextValue(Object2.class)); + Assert.assertEquals(null, ctxt.getContextValue(Object2.class, "named")); + + } + private static class MyJAXXObject extends DefaultJAXXContext implements JAXXObject { public Object getObjectById(String id) { @@ -208,6 +287,10 @@ public void removeDataBinding(String id) { } + public JAXXContext getDelegateContext() { + return null; + } + public void processDataBinding(String dest) { } Added: lutinjaxx/trunk/jaxx-core/src/test/java/jaxx/runtime/swing/NavigationTreeModelTest.java =================================================================== --- lutinjaxx/trunk/jaxx-core/src/test/java/jaxx/runtime/swing/NavigationTreeModelTest.java (rev 0) +++ lutinjaxx/trunk/jaxx-core/src/test/java/jaxx/runtime/swing/NavigationTreeModelTest.java 2008-11-16 17:44:15 UTC (rev 1017) @@ -0,0 +1,398 @@ +package jaxx.runtime.swing; + +import jaxx.runtime.DefaultJAXXContext; +import jaxx.runtime.JAXXContext; +import jaxx.runtime.JAXXContextEntryDef; +import jaxx.runtime.swing.NavigationTreeModel.NavigationTreeNode; +import org.junit.Assert; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + + +/** + * Test du model de navigation. + * + * @author chemit + */ +public class NavigationTreeModelTest { + + + private static final String ROOT_CONTEXT = "$root"; + private static final String FAKE = "-fake"; + private static final String DOT = "."; + + @Test + public void testFindNode() throws Exception { + + NavigationTreeNode rootNode = new NavigationTreeNode(ROOT_CONTEXT, null, null, null); + + for (int i = 0; i < 4; i++) { + NavigationTreeNode sonNode = new NavigationTreeNode(getNodeContext(i), null, null, null); + rootNode.insert(sonNode, i); + for (int j = 0; j < 4; j++) { + NavigationTreeNode sonSonNode = new NavigationTreeNode(getNodeContext(i, j), null, null, null); + sonNode.insert(sonSonNode, j); + for (int k = 0; k < 4; k++) { + sonSonNode.insert(new NavigationTreeNode(getNodeContext(i, j, k), null, null, null), k); + } + } + } + + NavigationTreeModel model = new NavigationTreeModel(rootNode); + + NavigationTreeNode node; + String contextPath; + String currentNode; + + contextPath = ROOT_CONTEXT; + node = model.findNode(contextPath); + assertNodeEquals(contextPath, ROOT_CONTEXT, 0, node, true); + + node = model.findNode(ROOT_CONTEXT + FAKE); + Assert.assertNull(node); + + for (int i = 0; i < 4; i++) { + currentNode = getNodeContext(i); + contextPath = ROOT_CONTEXT + DOT + currentNode; + node = model.findNode(contextPath); + assertNodeEquals(contextPath, currentNode, 1, node, false); + + for (int j = 0; j < 4; j++) { + currentNode = getNodeContext(i, j); + contextPath = ROOT_CONTEXT + DOT + getNodeContext(i) + DOT + currentNode; + node = model.findNode(contextPath); + assertNodeEquals(contextPath, currentNode, 2, node, false); + + for (int k = 0; k < 4; k++) { + currentNode = getNodeContext(i, j, k); + contextPath = ROOT_CONTEXT + DOT + getNodeContext(i) + DOT + getNodeContext(i, j) + DOT + currentNode; + node = model.findNode(contextPath); + assertNodeEquals(contextPath, currentNode, 3, node, false); + } + + node = model.findNode(ROOT_CONTEXT + DOT + getNodeContext(i) + DOT + getNodeContext(i, j) + DOT + currentNode + FAKE); + Assert.assertNull(node); + } + + node = model.findNode(ROOT_CONTEXT + DOT + getNodeContext(i) + DOT + currentNode + FAKE); + Assert.assertNull(node); + + } + + } + + /** + * Test the {@link NavigationTreeModel#getJAXXContextValue(jaxx.runtime.JAXXContext, String)} with an entry point + * as a bean. + * <p/> + * Tree is like this + * <pre> + * $root + + * - name <-- attached to context entry : java.lang.String.class,"name" + * - name2 <-- attached to context entry : java.lang.String.class,"name2" + * - model + <-- attached to context entry : Model.class,null + * - name + * -integerValue + * - sons + + * - 0 + + * - name + * - integerValue + * - sons + * - 1 + + * - name + * - integerValue + * - sons + * - 2 + + * - name + * - integerValue + * - sons + * </pre> + * <p/> + * With this tree, we will have to results : + * <pre> + * $root.name => context.get(String.class,"name") + * $root.name2 => context.get(String.class,"name2") + * $root.model => context.get(Model.class) + * $root.model.name => context.get(Model.class).getName() + * $root.model.integerValue => context.get(Model.class).getIntegerValue() + * $root.model.sons => context.get(Model.class).getSons() + * $root.model.sons.0 => context.get(Model.class).getSons().get(0) + * </pre> + * + * @throws Exception if any pb + */ + @Test + public void testGetJAXXContextValue() throws Exception { + + NavigationTreeNode rootNode = new NavigationTreeNode(ROOT_CONTEXT, null, null, null); + NavigationTreeNode sonNode; + NavigationTreeNode sonSonNode; + NavigationTreeNode sonSonSonNode; + + sonNode = new NavigationTreeNode("name", null, null, JAXXContextEntryDef.newDef("name", String.class)); + + rootNode.insert(sonNode, 0); + + sonNode = new NavigationTreeNode("name2", null, null, JAXXContextEntryDef.newDef("name2", String.class)); + + rootNode.insert(sonNode, 1); + + sonNode = new NavigationTreeNode("model", null, null, JAXXContextEntryDef.newDef(Model.class)); + + rootNode.insert(sonNode, 2); + + sonSonNode = new NavigationTreeNode("name", null, null, null); + sonNode.insert(sonSonNode, 0); + + sonSonNode = new NavigationTreeNode("integerValue", null, null, null); + sonNode.insert(sonSonNode, 1); + + sonSonNode = new NavigationTreeNode("sons", null, null, null); + sonNode.insert(sonSonNode, 2); + + sonSonSonNode = new NavigationTreeNode(0 + "", null, null, null); + sonSonNode.insert(sonSonSonNode, 0); + sonSonSonNode.insert(new NavigationTreeNode("name", null, null, null), 0); + sonSonSonNode.insert(new NavigationTreeNode("integerValue", null, null, null), 1); + sonSonSonNode.insert(new NavigationTreeNode("sons", null, null, null), 2); + + sonSonSonNode = new NavigationTreeNode(1 + "", null, null, null); + sonSonNode.insert(sonSonSonNode, 1); + sonSonSonNode.insert(new NavigationTreeNode("name", null, null, null), 0); + sonSonSonNode.insert(new NavigationTreeNode("integerValue", null, null, null), 1); + sonSonSonNode.insert(new NavigationTreeNode("sons", null, null, null), 2); + + sonSonSonNode = new NavigationTreeNode(2 + "", null, null, null); + sonSonNode.insert(sonSonSonNode, 2); + sonSonSonNode.insert(new NavigationTreeNode("name", null, null, null), 0); + sonSonSonNode.insert(new NavigationTreeNode("integerValue", null, null, null), 1); + sonSonSonNode.insert(new NavigationTreeNode("sons", null, null, null), 2); + + NavigationTreeModel model = new NavigationTreeModel(rootNode); + + JAXXContext context = new DefaultJAXXContext(); + context.setContextValue("the name", "name"); + context.setContextValue("the name2", "name2"); + + + context.setContextValue( + new Model("modelName", 10, + Arrays.asList( + new Model("one", 1, Collections.<Model>emptyList()), + new Model("two", 2, Collections.<Model>emptyList()), + new Model("three", 3, Collections.<Model>emptyList()) + ) + ) + ); + + Assert.assertNull(model.getJAXXContextValue(context, "$root.name" + FAKE)); + + testBinding(model, context, "$root.name", context.getContextValue(String.class, "name")); + testBinding(model, context, "$root.name2", context.getContextValue(String.class, "name2")); + + Model bean = context.getContextValue(Model.class); + + testBinding(model, context, "$root.model", bean); + testBinding(model, context, "$root.model.name", bean.getName()); + testBinding(model, context, "$root.model.integerValue", bean.getIntegerValue()); + testBinding(model, context, "$root.model.sons", bean.getSons()); + + testBinding(model, context, "$root.model.sons.0.name", bean.getSons().get(0).getName()); + testBinding(model, context, "$root.model.sons.0.integerValue", bean.getSons().get(0).getIntegerValue()); + testBinding(model, context, "$root.model.sons.0.sons", bean.getSons().get(0).getSons()); + + + testBinding(model, context, "$root.model.sons.1.name", bean.getSons().get(1).getName()); + testBinding(model, context, "$root.model.sons.1.integerValue", bean.getSons().get(1).getIntegerValue()); + testBinding(model, context, "$root.model.sons.1.sons", bean.getSons().get(1).getSons()); + + testBinding(model, context, "$root.model.sons.2.name", bean.getSons().get(2).getName()); + testBinding(model, context, "$root.model.sons.2.integerValue", bean.getSons().get(2).getIntegerValue()); + testBinding(model, context, "$root.model.sons.2.sons", bean.getSons().get(2).getSons()); + } + + /** + * Test the {@link NavigationTreeModel#getJAXXContextValue(jaxx.runtime.JAXXContext, String)} with an entry point + * as a list. + * <p/> + * Tree is like this + * <pre> + * $root + + * - models + <-- attached to context entry : java.util.List.class,"models" + * - 0 + + * - name + * -integerValue + * - sons + + * - 0 + + * - name + * - 1 + + * - name + * - integerValue + * - sons + * - 2 + + * - name + * - integerValue + * - sons + * </pre> + * <p/> + * With this tree, we will have to results : + * <pre> + * $root.models => context.get(List.class,"models") + * $root.models.0 => context.get(List.class,"models").get(0) + * $root.models.0.name => context.get(List.class,"models").get(0).getName() + * </pre> + * + * @throws Exception if any pb + */ + @Test + public void testGetJAXXContextValueFromList() throws Exception { + + NavigationTreeNode rootNode = new NavigationTreeNode(ROOT_CONTEXT, null, null, null); + NavigationTreeNode sonNode; + NavigationTreeNode sonSonNode; + NavigationTreeNode sonSonSonNode; + + // first son is a list of models + sonNode = new NavigationTreeNode("models", null, null, JAXXContextEntryDef.newListDef("models")); + rootNode.insert(sonNode, 0); + + // first son son is a model + sonSonNode = new NavigationTreeNode("0", null, null, null); + sonNode.insert(sonSonNode, 0); + + sonSonNode.insert(new NavigationTreeNode("name", null, null, null), 0); + sonSonNode.insert(new NavigationTreeNode("integerValue", null, null, null), 1); + sonSonNode.insert(sonSonSonNode = new NavigationTreeNode("sons", null, null, null), 2); + + sonSonSonNode.insert(sonSonSonNode = new NavigationTreeNode("0", null, null, null), 0); + sonSonSonNode.insert(new NavigationTreeNode("name", null, null, null), 0); + + // second son son is a model + sonSonNode = new NavigationTreeNode("1", null, null, null); + sonNode.insert(sonSonNode, 1); + sonSonNode.insert(new NavigationTreeNode("name", null, null, null), 0); + sonSonNode.insert(new NavigationTreeNode("integerValue", null, null, null), 1); + sonSonNode.insert(new NavigationTreeNode("sons", null, null, null), 2); + + // third son son is a model + sonSonNode = new NavigationTreeNode("2", null, null, null); + sonNode.insert(sonSonNode, 2); + sonSonNode.insert(new NavigationTreeNode("name", null, null, null), 0); + sonSonNode.insert(new NavigationTreeNode("integerValue", null, null, null), 1); + sonSonNode.insert(new NavigationTreeNode("sons", null, null, null), 2); + + NavigationTreeModel model = new NavigationTreeModel(rootNode); + + JAXXContext context = new DefaultJAXXContext(); + List<Model> list = new ArrayList<Model>(); + list.add(new Model("entryOne", 10, + Arrays.asList( + new Model("one", 1, Collections.<Model>emptyList()), + new Model("two", 2, Collections.<Model>emptyList()), + new Model("three", 3, Collections.<Model>emptyList()) + ) + )); + list.add(new Model("entryTwo", 20, + Arrays.asList( + new Model("2one", 1, Collections.<Model>emptyList()), + new Model("2two", 2, Collections.<Model>emptyList()), + new Model("2three", 3, Collections.<Model>emptyList()) + ) + )); + list.add(new Model("entryThree", 30, + Arrays.asList( + new Model("3one", 1, Collections.<Model>emptyList()), + new Model("3two", 2, Collections.<Model>emptyList()), + new Model("3three", 3, Collections.<Model>emptyList()) + ) + )); + context.setContextValue(list, "models"); + + Model bean; + + testBinding(model, context, "$root.models", list); + + bean = list.get(0); + testBinding(model, context, "$root.models.0", bean); + testBinding(model, context, "$root.models.0.name", bean.getName()); + testBinding(model, context, "$root.models.0.integerValue", bean.getIntegerValue()); + testBinding(model, context, "$root.models.0.sons", bean.getSons()); + testBinding(model, context, "$root.models.0.sons.0", bean.getSons().get(0)); + testBinding(model, context, "$root.models.0.sons.0.name", bean.getSons().get(0).getName()); + + bean = list.get(1); + testBinding(model, context, "$root.models.1", bean); + testBinding(model, context, "$root.models.1.name", bean.getName()); + testBinding(model, context, "$root.models.1.integerValue", bean.getIntegerValue()); + testBinding(model, context, "$root.models.1.sons", bean.getSons()); + + bean = list.get(2); + testBinding(model, context, "$root.models.2", bean); + testBinding(model, context, "$root.models.2.name", bean.getName()); + testBinding(model, context, "$root.models.2.integerValue", bean.getIntegerValue()); + testBinding(model, context, "$root.models.2.sons", bean.getSons()); + + } + + protected void testBinding(NavigationTreeModel model, JAXXContext context, String contextPath, Object expected) throws Exception { + + Object value; + value = model.getJAXXContextValue(context, contextPath); + Assert.assertNotNull(value); + Assert.assertEquals(expected, value); + } + + protected static String getNodeContext(int... context) { + String result = ""; + for (int i : context) { + result += i; + } + return result; + } + + protected void assertNodeEquals(String contextPath, String nodeContext, int level, NavigationTreeNode node, boolean root) { + //System.out.println(contextPath + " : " + (node == null ? null : node.getContextPath())); + Assert.assertNotNull(node); + Assert.assertEquals(root, node.isRoot()); + Assert.assertEquals(level, node.getLevel()); + Assert.assertEquals(nodeContext, node.getContext()); + Assert.assertEquals(contextPath, node.getContextPath()); + } + + public static class Model { + + protected String name; + + protected int integerValue; + + protected List<Model> sons; + + public Model(String name, int integerValue, List<Model> sons) { + this.name = name; + this.integerValue = integerValue; + this.sons = sons; + } + + public String getName() { + return name; + } + + public int getIntegerValue() { + return integerValue; + } + + public List<Model> getSons() { + return sons; + } + + @Override + public String toString() { + return super.toString() + "<name:" + name + ",integerValue:" + integerValue + ",sons: " + sons + ">"; + } + } + +} Modified: lutinjaxx/trunk/maven-jaxx-plugin/changelog =================================================================== --- lutinjaxx/trunk/maven-jaxx-plugin/changelog 2008-11-16 17:42:00 UTC (rev 1016) +++ lutinjaxx/trunk/maven-jaxx-plugin/changelog 2008-11-16 17:44:15 UTC (rev 1017) @@ -1,6 +1,7 @@ 0.6 chemit 200811?? + * 20081108 [chemit] can add extra imports in JaxxGeneratorMojo (will be added to all generated java files). * 20081104 [chemit] can add extra beanInfoSearchPath in JaxxGeneratorMojo - + ver-0-5 chemit 20081002 * 20081013 [chemit] can generate logger on jaxx files * 20081011 [chemit] improve site Modified: lutinjaxx/trunk/maven-jaxx-plugin/src/main/java/org/codelutin/jaxx/JaxxGeneratorMojo.java =================================================================== --- lutinjaxx/trunk/maven-jaxx-plugin/src/main/java/org/codelutin/jaxx/JaxxGeneratorMojo.java 2008-11-16 17:42:00 UTC (rev 1016) +++ lutinjaxx/trunk/maven-jaxx-plugin/src/main/java/org/codelutin/jaxx/JaxxGeneratorMojo.java 2008-11-16 17:44:15 UTC (rev 1017) @@ -178,6 +178,13 @@ */ protected String[] beanInfoSearchPath; + /** + * list of fqn of class toimport for all generated jaxx files + * + * @parameter expression="${jaxx.extraImports}" + */ + protected String[] extraImports; + protected String[] files; private static final String[] INCLUDES = {"**\\/*.jaxx"}; @@ -278,6 +285,7 @@ result.setI18nable(i18nable); result.setAddLogger(addLogger); result.setJaxxContextImplementorClass(jaxxContextImplementorClass); + result.setExtraImports(extraImports); return result; }
participants (1)
-
tchemit@users.labs.libre-entreprise.org