public class CGConfig {
	
	public String picoPrefix;
	
	public String picoServiceGroup;
	
	public boolean nanoPrivateField;
	
	// for demo
	public boolean eBaySOAService;
	public boolean eBayShoppingAPI;
	public boolean eBayTradingAPI;
	
	public boolean isNanoPrivateField() {
		return nanoPrivateField;
	}
	
	public boolean isEBaySOAService() {
		return eBaySOAService;
	}
	
	public boolean isEBayShoppingAPI() {
		return eBayShoppingAPI;
	}
	
	public boolean isEBayTradingAPI() {
		return eBayTradingAPI;
	}
} public class CGModel {
	
	private final List<ClassInfo> classes = new ArrayList<ClassInfo>();
	
	private final List<EnumInfo> enums = new ArrayList<EnumInfo>();

    /**
     *  Class model for codegen
     * @return List<ClassInfo>
     */
	public List<ClassInfo> getClasses() {
		return classes;
	}
	
    /**
     *  Enum model for codegen
     * @return List<EnumInfo>
     */
	public List<EnumInfo> getEnums() {
		return enums;
	}
	
} public class ClassInfo {
	
	// package name of this class
	private String packageName;
	// simple name of this class
	private String name;
	// full name of this class
	private String fullName;
	// is this an abstract class
	private boolean isAbstract;
	
	// the super class this class extends
	private TypeInfo superClass;
	// doc comment of this class
	private String docComment;
	
	// is this a nest class?
	private boolean nestClass = false;
	
	private RootElementAnnotation rootElementAnnotation;
	
	private XmlTypeAnnotation xmlTypeAnnotation;
	
	// fields of this class
	private final List<FieldInfo> fields = new ArrayList<FieldInfo>();
	
	
	/**
	 * A list of fields this class contains
	 * 
	 * @return a list of {@link FieldInfo} instances
	 */
	public List<FieldInfo> getFields() {
		return fields;
	}
	
	/**
	 * the package name of this class
	 * 
	 * @return package name
	 */
	public String getPackageName() {
		return packageName;
	}
	
	/**
	 * set the package name of this class
	 * @param packageName
	 */
	public void setPackageName(String packageName) {
		this.packageName = packageName;
	}
	
	/**
	 * get simple name of this class
	 * 
	 * @return simple name
	 */
	public String getName() {
		return name;
	}
	
	/**
	 * set simple name of this class
	 * 
	 * @param simple name
	 */
	public void setName(String name) {
		this.name = name;
	}
	
	/**
	 * get full name of this class
	 * 
	 * @return full name
	 */
	public String getFullName() {
		return fullName;
	}
	
	/**
	 * set full name of this class
	 * 
	 * @param fullName, the full name of this class
	 */
	public void setFullName(String fullName) {
		this.fullName = fullName;
	}
	
	/**
	 * abstract class or not
	 * 
	 * @return abstract class or not
	 */
	public boolean isAbstract() {
		return isAbstract;
	}
	
	/**
	 * set abstract class or not
	 * 
	 * @param isAbstract
	 */
	public void setAbstract(boolean isAbstract) {
		this.isAbstract = isAbstract;
	}
	
	/**
	 * get super class type if this class extends any super class
	 * 
	 * @return super class type, null if this class does not explicitly extend any super class
	 */
	public TypeInfo getSuperClass() {
		return superClass;
	}
	
	/**
	 * set super class type if this class extends any super class
	 * 
	 * @param superClass
	 */
	public void setSuperClass(TypeInfo superClass) {
		this.superClass = superClass;
	}
	
	/**
	 * doc comment of this class, extracted from xsd annotation
	 * @return doc comment
	 */
	public String getDocComment() {
		return docComment;
	}
	
	/**
	 * set doc comment of this class
	 * 
	 * @param docComment
	 */
	public void setDocComment(String docComment) {
		this.docComment = docComment;
	}
	
	public RootElementAnnotation getRootElementAnnotation() {
		return rootElementAnnotation;
	}

	public void setRootElementAnnotation(RootElementAnnotation rootElementAnnotation) {
		this.rootElementAnnotation = rootElementAnnotation;
	}
	
	public XmlTypeAnnotation getXmlTypeAnnotation() {
		return xmlTypeAnnotation;
	}

	public void setXmlTypeAnnotation(XmlTypeAnnotation xmlTypeAnnotation) {
		this.xmlTypeAnnotation = xmlTypeAnnotation;
	}

	/**
	 * Is this a nest class or not
	 * 
	 * @return true if nest class, false otherwise 
	 */
	public boolean isNestClass() {
		return nestClass;
	}

	/**
	 * Set is this a nest class or not
	 * 
	 * @param nestClass
	 */
	public void setNestClass(boolean nestClass) {
		this.nestClass = nestClass;
	}
} public class EnumInfo implements Serializable {
	
	private static final long serialVersionUID = -683805101371156468L;
	
	// package name of this enum type
	private String pkgName;
	// simple name of this enum type
	private String name;
	// full name of this enum type
	private String fullName;
	// doc comment of this enum type
	private String docComment;
	// a list of enum constant this enum contains
	private final List<EnumConstantInfo> enumConstants = new ArrayList<EnumConstantInfo>();
	
	/**
	 * the package name of this enum type
	 * @return package name
	 */
	public String getPackageName() {
		return pkgName;
	}
	/**
	 * set the package name of this enum type
	 * @param pkgName
	 */
	public void setPackageName(String pkgName) {
		this.pkgName = pkgName;
	}
	/**
	 * get the simple name of this enum type
	 * @return the simple name of this enum
	 */
	public String getName() {
		return name;
	}
	/**
	 * set the simple name of this enum type
	 * @param simple name
	 */
	public void setName(String name) {
		this.name = name;
	}
	/**
	 * the enum constants this enum contains
	 * @return a list of enum constants
	 */
	public List<EnumConstantInfo> getEnumConstants() {
		return enumConstants;
	}
	/**
	 * the doc comment of this enum type
	 * @return doc comment
	 */
	public String getDocComment() {
		return docComment;
	}
	/**
	 * set the doc comment of this enum type
	 * @param docComment
	 */
	public void setDocComment(String docComment) {
		this.docComment = docComment;
	}
	
	/**
	 * Get full name of this enum
	 * 
	 * @return full name
	 */
	public String getFullName() {
		return fullName;
	}
	
	/**
	 * set full name of this enum
	 * 
	 * @param fullName
	 */
	public void setFullName(String fullName) {
		this.fullName = fullName;
	}	
} public class FieldInfo {

	// name of this field
	private String name;
	
	// type of this field
	private TypeInfo type;
	
	// doc comments
	private String docComment;
	
	// is schema element, map to xml element
	private boolean isElement = false;
	// is schema attribute, map to xml attribute
	private boolean isAttribute = false;
	// is schema value, map to xml text value
	private boolean isValue = false;
	// is schema any, map to any
	private boolean isAny = false;
	
	private ElementAnnotation elementAnnotation;
	
	private AttributeAnnotation attributeAnnotation;
	
	/**
	 * doc comment of this field
	 * @return doc comment
	 */
	public String getDocComment() {
		return docComment;
	}
	/**
	 * set doc comment of this field
	 * @param docComment
	 */
	public void setDocComment(String docComment) {
		this.docComment = docComment;
	}
	/**
	 * simple name of this field
	 * @return simple name
	 */
	public String getName() {
		return name;
	}
	/**
	 * set simple name of this field
	 * @param name
	 */
	public void setName(String name) {
		this.name = name;
	}
	/**
	 * type of this field
	 * @return {@link TypeInfo} instance
	 */
	public TypeInfo getType() {
		return type;
	}
	/**
	 * set type of this field
	 * @param type
	 */
	public void setType(TypeInfo type) {
		this.type = type;
	}
	/**
	 * whether this field maps to xml element 
	 * @return whether this filed maps to xml element
	 */
	public boolean isElement() {
		return isElement;
	}
	/**
	 * set whether this field maps to xml element
	 * @param isElement
	 */
	public void setElement(boolean isElement) {
		this.isElement = isElement;
	}
	/**
	 * whether this field maps to xml attribute
	 * @return whether this field maps to xml attribute
	 */
	public boolean isAttribute() {
		return isAttribute;
	}
	/**
	 * set whether this field maps to xml attribute
	 * @param isAttribute
	 */
	public void setAttribute(boolean isAttribute) {
		this.isAttribute = isAttribute;
	}
	/**
	 * whether this field maps to xml text value
	 * @return whether this field maps to xml text value
	 */
	public boolean isValue() {
		return isValue;
	}
	/**
	 * set whether this field maps to xml text value
	 * @param isValue
	 */
	public void setValue(boolean isValue) {
		this.isValue = isValue;
	}
	/**
	 * whether this field maps to xml any
	 * @return whether this field maps to xml any
	 */
	public boolean isAny() {
		return isAny;
	}
	/**
	 * set whether this field maps to xml any
	 * @param isAny
	 */
	public void setAny(boolean isAny) {
		this.isAny = isAny;
	}
	
	public ElementAnnotation getElementAnnotation() {
		return elementAnnotation;
	}
	
	public void setElementAnnotation(ElementAnnotation elementAnnotation) {
		this.elementAnnotation = elementAnnotation;
	}
	
	public AttributeAnnotation getAttributeAnnotation() {
		return attributeAnnotation;
	}
	
	public void setAttributeAnnotation(AttributeAnnotation attributeAnnotation) {
		this.attributeAnnotation = attributeAnnotation;
	}	
} public class FileInfo {

	// name of the file
	private String name;

	// suffix of the file name
	private String suffix;

	// relative path of the file
	private String path;

	// content of the file
	private byte[] content;

	public String getName() {
		return name;
	}

	/**
	 * Set the name of the file
	 * 
	 * @param name
	 */
	public void setName(String name) {
		this.name = name;
	}

	public String getSuffix() {
		return suffix;
	}

	/**
	 * Set the suffix of the file
	 * 
	 * @param suffix
	 */
	public void setSuffix(String suffix) {
		this.suffix = suffix;
	}

	/**
	 * The relative path of this file, derived from package name,
	 * 
	 * may return null if the file resides in default pacakge.
	 * 
	 * @return
	 */
	public String getPath() {
		return path;
	}

	/**
	 * Set the relative path of the file
	 * 
	 * @param path
	 */
	public void setPath(String path) {
		this.path = path;
	}

	public byte[] getContent() {
		return content;
	}
	
	/**
	 * Set the content of the file
	 * 
	 * @param content
	 */
	public void setContent(byte[] content) {
		this.content = content;
	}
	
	/**
	 * full name of the file(name.suffix)
	 * @return full name
	 */
	public String getFullname() {
		return name + "." + suffix;
	}
	
	/**
	 * Does this file resides in the default package
	 * 
	 * @return true if this file resides in the default package, false otherwise
	 */
	public boolean isInDefaultPackage() {
		if (path == null || path.trim().length() == 0) {
			return true;
		} else {
			return false;
		}
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		result = prime * result + ((path == null) ? 0 : path.hashCode());
		result = prime * result + ((suffix == null) ? 0 : suffix.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		FileInfo other = (FileInfo) obj;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		if (path == null) {
			if (other.path != null)
				return false;
		} else if (!path.equals(other.path))
			return false;
		if (suffix == null) {
			if (other.suffix != null)
				return false;
		} else if (!suffix.equals(other.suffix))
			return false;
		return true;
	}
} public class TypeInfo implements Serializable {
	
	private static final long serialVersionUID = 7174500180688531754L;

	// simple name of this type
	private String name;
	
	// full name(fqn) of type
	private String fullName;
	
	// is this a primitive type
	private boolean isPrimitive = false;
	
	// is this an enum type
	private boolean isEnum = false;
	
	// is this an array type
	private boolean isArray = false;
	
	// is collection
	private boolean isCollection = false;
	
	// component type of the array(if this is an array type) 
	private TypeInfo elementType = null;
	
	// is this a nest class?
	private boolean nestClass = false;
	

	// type parameters of this type
	private final List<TypeInfo> typeParameters = new ArrayList<TypeInfo>() ;
	
	/**
	 * whether this is an enum type
	 * @return whether this is an enum type
	 */
	public boolean isEnum() {
		return isEnum;
	}
	
	/**
	 * set whether this is an enum type
	 * @param isEnum
	 */
	public void setEnum(boolean isEnum) {
		this.isEnum = isEnum;
	}
	
	/**
	 * whether this is an array type
	 * @return whether this is an array type
	 */
	public boolean isArray() {
		return isArray;
	}

	/**
	 * set whether this is an array type
	 * @param isArray
	 */
	public void setArray(boolean isArray) {
		this.isArray = isArray;
	}

	/**
	 * component type of the array(if this is an array type)
	 * T of T[]
	 * @return TypeInfo instance
	 */
	public TypeInfo getElementType() {
		return elementType;
	}

	/**
	 * set component type of the array(if this is an array type)
	 * @param elementType
	 */
	public void setElementType(TypeInfo elementType) {
		this.elementType = elementType;
	}

	/**
	 * whether this is a primitive type
	 * @return whether this is a primitive type
	 */
	public boolean isPrimitive() {
		return isPrimitive;
	}

	/**
	 * set whether this is a primitive type
	 * @param isPrimitive
	 */
	public void setPrimitive(boolean isPrimitive) {
		this.isPrimitive = isPrimitive;
	}

	/**
	 * simple name of this type
	 * @return simple name
	 */
	public String getName() {
		return name;
	}

	/**
	 * set simple name of this type
	 * @param simple name
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * full name(fqn) of this type
	 * @return full name
	 */
	public String getFullName() {
		return fullName;
	}

	/**
	 * set full name(fqn) of this type
	 * @param fullName
	 */
	public void setFullName(String fullName) {
		this.fullName = fullName;
	}

	/**
	 * type paramters of this type
	 * @return a list of {@link TypeInfo} instances
	 */
	public List<TypeInfo> getTypeParameters() {
		return typeParameters;
	}
	
	/**
	 * whether this field is a collection
	 * @return whether this field is a collection
	 */
	public boolean isCollection() {
		return isCollection;
	}
	/**
	 * set whether this field is a collection
	 * @param isCollection
	 */
	public void setCollection(boolean isCollection) {
		this.isCollection = isCollection;
	}
	
	/**
	 * Is this a nest class or not
	 * 
	 * @return true if nest class, false otherwise 
	 */
	public boolean isNestClass() {
		return nestClass;
	}

	/**
	 * Set is this a nest class or not
	 * 
	 * @param nestClass
	 */
	public void setNestClass(boolean nestClass) {
		this.nestClass = nestClass;
	}
	
} public abstract class AbstractClientModule implements ClientModule {
	
	private ErrorReceiver errorReceiver;

	/**
	 * Get freemarker datamodel
	 * 
	 * @return a new SimpleHash instance
	 */
	protected SimpleHash getFreemarkerModel() {
		return new SimpleHash();
	}
	
	/**
	 * Set ErrorReceiver instance to be used for error reporting
	 */
	public void setErrorReceiver(ErrorReceiver errorReceiver) {
		this.errorReceiver = errorReceiver;
	}
	
	/**
	 * Get a template URL for the template of the given name.
	 * 
	 * @param template
	 *            The specified template.
	 * @return The URL to the specified template.
	 * @throws XjcModuleException
	 */
	abstract protected URL getTemplateURL(String template) throws XjcModuleException;
	

	/**
	 * Generate files according to specific template, datamodel and file
	 * information,
	 * 
	 * No cache for this interface
	 * 
	 * @param template
	 *            , template URL
	 * @param fmModel
	 *            , freemarker datamodel
	 * @param fileName
	 *            , file name
	 * @param suffix
	 *            , file suffix
	 * @param relativePath
	 *            , file relative path(such as a\b\c)         
	 * @return FileInfo instance
	 * @throws XjcModuleException
	 */
	protected FileInfo generateFile(URL template, Object fmModel,
			String fileName, String suffix, String relativePath)
			throws XjcModuleException {
		byte[] context = processTemplate(template, fmModel);
		FileInfo fileInfo = new FileInfo();
		fileInfo.setName(fileName);
		fileInfo.setPath(relativePath);
		fileInfo.setSuffix(suffix);
		fileInfo.setContent(context);
		return fileInfo;
	}

	/**
	 * Process given template and datamodel
	 * 
	 * @param templateURL
	 *            , template URL
	 * @param model
	 *            , freemarker datamodel
	 * @return byte array
	 * @throws XjcModuleException
	 */
	protected byte[] processTemplate(URL templateURL, Object model)
			throws XjcModuleException {
		try {
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			FreemarkerProcessor.getInstance().processTemplate(templateURL, model, baos);
			return baos.toByteArray();
		} catch (XjcModuleException e) {
			throw new XjcModuleException("Fail to process template "
					+ templateURL, e);
		}
	}
	
	/**
	 * information level report
	 * 
	 * @param msg
	 */
	protected void info(String msg) {
		if (this.errorReceiver != null) {
			this.errorReceiver.debug(msg);
		}
	}
	
	/**
	 * error level report
	 * 
	 * @param msg
	 */
	protected void error(String msg) {
		if (this.errorReceiver != null) {
			LocatorImpl locator = new LocatorImpl();
			locator.setLineNumber(-1);
			locator.setSystemId("module : " + this.getName().toString());
			this.errorReceiver.error(locator, msg);
		}
	}
	
	protected void warn(String msg) {
		if (this.errorReceiver != null) {
			LocatorImpl locator = new LocatorImpl();
			locator.setLineNumber(-1);
			locator.setSystemId("module : " + this.getName().toString());
			this.errorReceiver.warning(locator, msg);
		}
	}
	
	/**
	 * error level report
	 * 
	 * @param msg
	 */
	protected void debug(String msg) {
		if (this.errorReceiver != null) {
			this.errorReceiver.debug(msg);
		}
	}

} public enum ModuleName {

	NANO("nano"),  // android java
	MICRO("micro"), // php
	PICO("pico");  // ios objective-c
	
	private final String name;
	
	private ModuleName(String n) {
		name = n;
	}
	
	public String toString() {
		return name;
	}
	
	public static ModuleName fromString(String value) {
		if (value != null) {
			for(ModuleName moduleName : ModuleName.values()) {
				if (value.equalsIgnoreCase(moduleName.name)) {
					return moduleName;
				}
			}
		}
		return null;
	}
	
} public class XjcModuleException extends Exception {
	private static final long serialVersionUID = 1L;

	public XjcModuleException() {
	}

	public XjcModuleException(String message) {
		super(message);
	}

	public XjcModuleException(Throwable cause) {
		super(cause);
	}

	public XjcModuleException(String message, Throwable cause) {
		super(message, cause);
	}
} public final class ClassNameUtil {
	
	/**
	 * Strip qualifier of a fully qualified class name,
	 * 
	 * @param className, fully qualified class name, such as java.util.List
	 * @return qualified stripped class name, such List
	 */
    public static String stripQualifier(String className) {
        String qual = getQualifier(className);
        if (qual == null) return className;
        int len = className.length();
        return className.substring(qual.length() + 1, len);
    }

    /**
     * 
     * Get package name a fully qualified class name
     * 
     * @param className, fully qualified class anme, such as java.util.List
     * @return package name, such as java.util
     */
    public static String getPackageName(String className) {
        String packageName = getQualifier(className);
        return packageName != null ? packageName : "";
    }
	
	/**
	 * Get qualifier of a class name
	 * 
	 * @param className, full qualified class name, such as java.util.List
	 * @return String qualifier, such as java.util
	 */
    public static String getQualifier(String className) {
    	String name = className;
    	if (name.indexOf('<') >= 0) {//is generic class?
    		name = erase(name);
    	}
        int index = name.lastIndexOf('.');
        return (index < 0) ? null : name.substring(0, index);
    }
    
    /**
     *
     * Erase type parameters of a generic class
     *
     * @param className Generic class name, such as java.util.List<java.lang.String>
     * @return type parameters erased class name, such as java.util.List
     */
    public static String erase(String className) {
       int index = className.indexOf('<');
       if(index < 0)
           return className;
       return (index > 0)?className.substring(0, index):className;
    }
    
    /**
     * change package name into a relative file path
     * @param pkgName, the name of the package
     * @return relative file path
     */
    public static String packageNameToPath(String pkgName) {
    	if (pkgName == null) return null;
    	String path = pkgName.replace('.', File.separatorChar);
    	return path;
    }

}
import java.net.URL;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.xml.datatype.XMLGregorianCalendar;
import com.leansoft.mxjc.model.CGConfig;
import com.leansoft.mxjc.model.CGModel;
import com.leansoft.mxjc.model.ClassInfo;
import com.leansoft.mxjc.model.EnumInfo;
import com.leansoft.mxjc.model.FieldInfo;
import com.leansoft.mxjc.model.FileInfo;
import com.leansoft.mxjc.model.TypeInfo;
import com.leansoft.mxjc.module.AbstractClientModule;
import com.leansoft.mxjc.module.ModuleName;
import com.leansoft.mxjc.module.XjcModuleException;
import com.leansoft.mxjc.util.ClassNameUtil;
import freemarker.template.SimpleHash;
package com.leansoft.mxjc.module.nano;





/**
 * <h1>Nano Client Module</h1>
 * 
 * @author bulldog
 *
 */
public class NanoClientModule extends AbstractClientModule {
	
	private static final String JAVA_DEFAULT_PACKAGE_NAME = "java.lang";
	private static final String TEMPLATE_FOLDER = "template";
	
	private Map<String, String> typeMapping = new HashMap<String, String>();
	
	// references to templates
	private URL classTemplate;
	private URL enumTemplate;

	@Override
	public ModuleName getName() {
		return ModuleName.NANO;
	}

	@Override
	public void init() throws XjcModuleException {
		info("NanoClientModule loading templates ...");
		loadTemplates();
		
		// some custom type mappings
		// android does not fully support these data types yet
		typeMapping.put(XMLGregorianCalendar.class.getName(),
				"java.util.Date");
		typeMapping.put(javax.xml.datatype.Duration.class.getName(),
				"com.leansoft.nano.custom.types.Duration");
	}
	
	private void loadTemplates() throws XjcModuleException {
		classTemplate = getTemplateURL("client-class-type.fmt");
		enumTemplate = getTemplateURL("client-enum-type.fmt");
	}

	@Override
	public Set<FileInfo> generate(CGModel cgModel, CGConfig config) throws XjcModuleException {