/* * Copyright (c) 2009, Dennis M. Sosnoski. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the * following conditions are met: * * Redistributions of source code must retain the above copyright notice, this list of conditions and the following * disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the * following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of * JiBX nor the names of its contributors may be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.dataone.jibx.schema.codegen.extend; import java.util.Iterator; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ASTParser; import org.eclipse.jdt.core.dom.Block; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.FieldDeclaration; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.MethodInvocation; import org.eclipse.jdt.core.dom.SingleVariableDeclaration; import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.jdt.core.dom.VariableDeclarationFragment; import org.jibx.binding.model.ElementBase; import org.jibx.schema.codegen.IClassHolder; import org.jibx.schema.codegen.NameUtils; import org.jibx.schema.codegen.extend.ClassDecorator; import org.jibx.schema.codegen.extend.NameMatchDecoratorBase; import org.jibx.util.NameUtilities; /** * Code generation decorator which adds the * java.lang.Serializable interface to each class, and optionally also adds a * private static final long serialVersionUID value. */ public class SimpleConstructorDecorator extends FieldMatchDecoratorBase implements ClassDecorator { static private final String SIMPLE_TYPES = "((?:Boolean)|(?:Byte)|(?:Character)|(?:Double)|(?:Float)|(?:Integer)|(?:Long)|(?:BigDecimal)|(?:BigInteger)|(?:Short)|(?:String)|(?:byte)|(?:short)|(?:int)|(?:long)|(?:float)|(?:double)|(?:boolean)|(?:char))$"; Pattern pattern = Pattern.compile(SIMPLE_TYPES); private static final String s_classText = "class Gorph {\n" + "/** Default Constructor for a generated class\n" + "* \n " + "**/\n" + "public $5() {\n" + "//do nothing in here\n" + "$1 = null;\n" + "}\n" + "\n" + "/** $5 Constructor that takes a single field\n" + "* @param $1 is $3.\n " + "**/\n" + "public $5($3 $1) { " + "this.set$2($1);" + "}\n" + "\n"; // where $0 is the description text, $1 is the field name, $2 is the value name with initial uppercase character, // $3 is the type, and $4 is a cast if an untyped list is used, or empty if a typed list is used // $5 is the class name /** * Parser instance used by class. */ private final ASTParser m_parser = ASTParser.newParser(AST.JLS3); /** * * Default constructor to initialize * must call super to include default fields * */ public SimpleConstructorDecorator() { super(); } /** * Replace all occurrences of one string with another in a buffer. * * @param match * @param replace * @param buff */ private static void replace(String match, String replace, StringBuffer buff) { int base = 0; while ((base = buff.indexOf(match, base)) >= 0) { buff.replace(base, base + match.length(), replace); } } /** * Method called after completing code generation for the target class. * * @param binding * @param holder */ public void finish(ElementBase binding, IClassHolder holder) { } /** * Method called before starting code generation for the target class. * * @param holder */ public void start(IClassHolder holder) { } /** * Method called after adding each data value to class. * * @param basename base name used for data value * @param collect repeated value flag * @param type value type (item value type, in the case of a repeated value) * @param field actual field * @param getmeth read access method (null if a flag value) * @param setmeth write access method (null if a flag value) * @param descript value description text * @param holder */ public void valueAdded(String basename, boolean collect, String type, FieldDeclaration field, MethodDeclaration getmeth, MethodDeclaration setmeth, String descript, IClassHolder holder) { Matcher simpleTypeMatcher = pattern.matcher(type); Boolean restrictFromBasename = true; // check the basename against m_fields, if it is reasonable to assume the basename // has been populated with anything other than the default value // restrictFromBasename is by default true which means // that if fields are not provided to allow the operation to continue // restrictFromBasename should only be set to false if // there are fields that have been added and // the basename is not matched if ((m_fields.size() > 1) && (!m_fields.contains(basename))) { restrictFromBasename = false; } System.out.println(basename + " has type " + type + " is collect " + collect + " has get " + getmeth.getName() + " has set " + setmeth.getName()); if (!collect && simpleTypeMatcher.find() && restrictFromBasename) { MethodDeclaration[] currentMethods = holder.getMethods(); Boolean hasConstructor = false; // find if there are any constructors, and if so, any constructors with arguments MethodDeclaration currentMethod = null; for (int i = 0; i < currentMethods.length; ++i) { currentMethod = currentMethods[i]; if (currentMethod.isConstructor()) { hasConstructor = true; if (!currentMethod.parameters().isEmpty()) { break; } } } if (hasConstructor) { // the currentMethod should have parameters already, if not something went wrong if (currentMethod.parameters().isEmpty()) { System.err.println("Something went horribly wrong with " + basename + " that has type " + type + " is collect " + collect + " has get " + getmeth.getName() + " has set " + setmeth.getName()); } else { AST ast = currentMethod.getAST(); SingleVariableDeclaration variableDeclaration = ast.newSingleVariableDeclaration(); variableDeclaration.setType(getFieldType(ast, type)); variableDeclaration.setName(ast.newSimpleName(basename)); currentMethod.parameters().add(variableDeclaration); Block valueBlockBody = currentMethod.getBody(); MethodInvocation currentMethodInvocation = ast.newMethodInvocation(); currentMethodInvocation.setExpression(ast.newThisExpression()); currentMethodInvocation.setName(ast.newSimpleName(setmeth.getName().toString())); currentMethodInvocation.arguments().add(ast.newSimpleName(basename)); valueBlockBody.statements().add(ast.newExpressionStatement(currentMethodInvocation)); } // add variable to the parameters and statements to the constructor method block } else { // create a constructor with arguments FieldDeclaration[] fieldDeclarationArray = holder.getFields(); // make substitutions in template text StringBuffer buff = new StringBuffer(s_classText); VariableDeclarationFragment vardecl = (VariableDeclarationFragment) field.fragments().get(0); replace("$0", descript, buff); replace("$1", vardecl.getName().getIdentifier(), buff); replace("$2", NameUtilities.depluralize(NameUtils.toNameWord(basename)), buff); replace("$3", type, buff); String cast = field.getType().isParameterizedType() ? "" : ("(" + type + ")"); replace("$4", cast, buff); replace("$5", holder.getName(), buff); // parse the resulting text m_parser.setSource(buff.toString().toCharArray()); CompilationUnit unit = (CompilationUnit) m_parser.createAST(null); // add all methods from output tree to class under construction TypeDeclaration typedecl = (TypeDeclaration) unit.types().get(0); for (Iterator iter = typedecl.bodyDeclarations().iterator(); iter.hasNext();) { ASTNode node = (ASTNode) iter.next(); if (node instanceof MethodDeclaration) { MethodDeclaration constructor = (MethodDeclaration) node; constructor.setConstructor(true); holder.addMethod(constructor); } } } } } }