/*
* Copyright 2006 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License")
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jvnet.jaxb2_commons.plugin.value_constructor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import org.xml.sax.ErrorHandler;
import com.sun.codemodel.JClass;
import com.sun.codemodel.JDefinedClass;
import com.sun.codemodel.JExpr;
import com.sun.codemodel.JFieldVar;
import com.sun.codemodel.JInvocation;
import com.sun.codemodel.JMethod;
import com.sun.codemodel.JMod;
import com.sun.codemodel.JVar;
import com.sun.tools.xjc.Options;
import com.sun.tools.xjc.Plugin;
import com.sun.tools.xjc.outline.ClassOutline;
import com.sun.tools.xjc.outline.Outline;
/**
* Generate two constructors for each generated class, one of which is a default constructor,
* the other takes an argument for each field in the class and initialises the field with the
* argument value.
*
* Without this plugin, XJC will not generate any explicit constructors.
*
* @author Kenny MacLeod
* $Id: XjcValueConstructorPlugin.java,v 1.7 2007-11-26 18:35:27 skaffman Exp $
*/
public class ValueConstructorPlugin extends Plugin
{
@Override
public String getOptionName()
{
return "Xvalue-constructor";
}
@Override
public String getUsage()
{
return " -Xvalue-constructor : enable generation of value constructors";
}
@Override
public boolean run(final Outline outline, final Options options, final ErrorHandler errorHandler) {
// For each defined class
for (final ClassOutline classOutline : outline.getClasses()) {
final JDefinedClass implClass = classOutline.implClass;
// Create the default, no-arg constructor
@SuppressWarnings("unused")
final JMethod defaultConstructor = implClass.constructor(JMod.PUBLIC);
defaultConstructor.javadoc().add("Default no-arg constructor");
defaultConstructor.body().invoke("super");
final Collection<JFieldVar> superClassInstanceFields = getInstanceFields(getSuperclassFields(implClass));
final Collection<JFieldVar> thisClassInstanceFields = getInstanceFields(implClass.fields().values());
final boolean doGenerateValueConstructor = !superClassInstanceFields.isEmpty() || !thisClassInstanceFields.isEmpty();
// If the class or its (generated) superclass has fields, then generate a value constructor
if (doGenerateValueConstructor) {
// Create the skeleton of the value constructor
final JMethod valueConstructor = implClass.constructor(JMod.PUBLIC);
valueConstructor.javadoc().add("Fully-initialising value constructor");
// If our superclass is also being generated, then we can assume it will also have
// its own value constructor, so we add an invocation of that constructor.
if (implClass._extends() instanceof JDefinedClass) {
final JInvocation superInvocation = valueConstructor.body().invoke("super");
// Add each argument to the super constructor.
for (JFieldVar superClassField : superClassInstanceFields) {
if (generateConstructorParameter(superClassField)) {
final JVar arg = valueConstructor.param(JMod.FINAL, superClassField.type(), superClassField.name());
superInvocation.arg(arg);
}
}
}
// Now add constructor parameters for each field in "this" class, and assign them to
// our fields.
for (final JFieldVar field : thisClassInstanceFields) {
if (generateConstructorParameter(field)) {
final JVar arg = valueConstructor.param(JMod.FINAL, field.type(), field.name());
valueConstructor.body().assign(JExpr.refthis(field.name()), arg);
}
}
}
}
return true;
}
/**
* Takes a collection of fields, and returns a new collection containing only the instance
* (i.e. non-static) fields.
*/
protected Collection<JFieldVar> getInstanceFields(final Collection<JFieldVar> fields) {
final List<JFieldVar> instanceFields = new ArrayList<JFieldVar>();
for (final JFieldVar fieldVar : fields) {
final boolean isStaticField = (fieldVar.mods().getValue() & JMod.STATIC) != 0;
if (!isStaticField) {
instanceFields.add(fieldVar);
}
}
return instanceFields;
}
/**
* Whether or not to generate a constructor parameter for the given field.
*/
protected boolean generateConstructorParameter(final JFieldVar field) {
final boolean isStaticField = (field.mods().getValue() & JMod.STATIC) > 0;
return !isStaticField;
}
/**
* Retrieve a List of the fields of each ancestor class. I walk up the class hierarchy
* until I reach a class that isn't being generated by JAXB.
*/
protected List<JFieldVar> getSuperclassFields(final JDefinedClass implClass) {
final List<JFieldVar> fieldList = new LinkedList<JFieldVar>();
JClass superclass = implClass._extends();
while (superclass instanceof JDefinedClass) {
fieldList.addAll(0, ((JDefinedClass)superclass).fields().values());
superclass = superclass._extends();
}
return fieldList;
}
}