/*
* Copyright (c) 2005
* XDoclet Team
* All rights reserved.
*/
package org.xdoclet.plugin.ejb;
import java.io.File;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.generama.MergeableVelocityTemplateEngine;
import org.generama.QDoxCapableMetadataProvider;
import org.generama.WriterMapper;
import org.generama.defaults.JavaGeneratingPlugin;
import org.xdoclet.plugin.ejb.qtags.TagLibrary;
import com.thoughtworks.qdox.JavaDocBuilder;
import com.thoughtworks.qdox.model.BeanProperty;
import com.thoughtworks.qdox.model.ClassLibrary;
import com.thoughtworks.qdox.model.JavaClass;
import com.thoughtworks.qdox.model.JavaClassCache;
import com.thoughtworks.qdox.model.JavaClassParent;
import com.thoughtworks.qdox.model.JavaSource;
import com.thoughtworks.qdox.model.Type;
/**
* Base JavaGeneratingPlugin for EjbRelated plugins<br>
* Contains code to override filename/classname creation for plugin to be
* able to be set using pattern/package/class tag values
*
* @qtags.ignore
*
* @author Diogo Quintela
* @version $Revision: 538 $
*/
public abstract class EjbJavaGeneratingPlugin extends JavaGeneratingPlugin implements EjbTypeResolver {
/** The log object */
protected final Log log;
// For now, we are forcing to use velocity, maybe sometime, we may need to
// pull down to a EjbVelocityJavaGeneratingPlugin, for example
private final MergeableVelocityTemplateEngine templateEngine;
/** Ejb Configuration object */
protected final EjbConfig config;
/** Ejb Utils */
protected final EjbUtils ejbUtils;
/** Directory of merge file(s) */
protected File mergeDir;
/** From Plugin#fileregex (private). Hacked to get a copy of */
protected String fileregex;
/** From Plugin#filereplace (private). Hacked to get a copy of */
protected String filereplace;
/** From Plugin#packageregex (private). Hacked to get a copy of */
protected String packageregex;
/** From Plugin#packagereplace (private). Hacked to get a copy of */
protected String packagereplace;
/** When false, files are only created when dirty. */
protected boolean force;
/** True if this plugin should explain what it's doing. */
protected boolean verbose;
public EjbJavaGeneratingPlugin(MergeableVelocityTemplateEngine templateEngine,
WriterMapper writerMapper, EjbConfig config) {
super(templateEngine, config.getMetadataProvider(), writerMapper);
this.templateEngine = templateEngine;
this.config = config;
this.ejbUtils = new EjbUtils(config);
this.log = getLog();
registerTagLibraries(metadataProvider);
if (log.isTraceEnabled()) {
log.trace(getClass().getName() + " constructor was called.");
}
}
protected void registerTagLibraries(QDoxCapableMetadataProvider provider) {
new TagLibrary(provider);
}
public void setFileregex(String fileregex) {
if (fileregex == null) {
throw new NullPointerException();
}
this.fileregex = fileregex;
super.setFileregex(fileregex);
}
protected void populateContextMap(Map map) {
super.populateContextMap(map);
map.put("util", getEjbUtils());
map.put("version", getVersion());
}
public EjbVersion getVersion() {
return getConfig().getEjbVersion();
}
public EjbUtils getEjbUtils() {
return ejbUtils;
}
public EjbConfig getConfig() {
return this.config;
}
public static Log getLog(Class clazz) {
return LogFactory.getLog(clazz);
}
public Log getLog() {
return getLog(getClass());
}
public Log getLogEx() {
StackTraceElement[] stack = new Exception().getStackTrace();
StackTraceElement callingMethod = stack[1];
return LogFactory.getLog(callingMethod.getClassName() + '.' + callingMethod.getMethodName());
}
public void setFilereplace(String filereplace) {
if (filereplace == null) {
throw new NullPointerException();
}
this.filereplace = filereplace;
super.setFilereplace(filereplace);
}
public void setPackageregex(String packageregex) {
if (packageregex == null) {
throw new NullPointerException();
}
this.packageregex = packageregex;
super.setPackageregex(packageregex);
}
public void setPackagereplace(String packagereplace) {
if (packagereplace == null) {
throw new NullPointerException();
}
this.packagereplace = packagereplace;
super.setPackagereplace(packagereplace);
}
protected String getLocalyDefinedFullClassName(JavaClass clazz) {
// to be overriden if needed
return null;
}
protected String getLocalyDefinedPackageName(JavaClass clazz) {
// to be overriden if needed
return null;
}
protected String getPatternBasedUnqualifiedName(JavaClass clazz) {
// to be overriden if needed
return null;
}
/**
* The physical type for the generated java type
*
* @param clazz The source metadata class
*
* @return The type to use
*/
public EjbJavaType getPhysicalType(JavaClass clazz) {
EjbJavaType retType = null;
String fullType = getLocalyDefinedFullClassName(clazz);
if (fullType != null) {
retType = new EjbJavaType(fullType);
}
if (retType == null) {
String unqualifiedName = getPatternBasedUnqualifiedName(clazz);
if (unqualifiedName == null) {
unqualifiedName = clazz.getName();
unqualifiedName = unqualifiedName.replaceAll(fileregex, filereplace);
}
String javaPackage = getLocalyDefinedPackageName(clazz);
if (javaPackage == null) {
javaPackage = clazz.getPackage();
javaPackage = javaPackage.replaceAll(packageregex, packagereplace);
}
retType = new EjbJavaType(javaPackage, unqualifiedName);
}
return retType;
}
/**
* The type to be referenced to, normally equals to physical generated type, this should be called when
* some plugin tries to refer to the generate class by this plugin<br>
* This supports a type that extends the physical type to be used.<br>
*
* See @ejb.bean impl-class-name for example (not directly related ...)
*
* @param clazz The source metadata class
*
* @return The type to use
*/
public EjbJavaType getVirtualType(JavaClass clazz) {
return getPhysicalType(clazz);
}
public String getDestinationFilename(Object metadata) {
return getPhysicalType((JavaClass) metadata).getName() + ".java";
}
public String getDestinationPackage(Object metadata) {
return getPhysicalType((JavaClass) metadata).getPackage();
}
/**
* A method called right before generation is called (one time if multiOutput is false, several otherwise).
* Allows for late setting of directories for example in MergeableVelocityTemplateEngine
*
* @see MergeableVelocityTemplateEngine#addPath(String)
*/
protected void preGenerate() {
if ((mergeDir != null) && mergeDir.isDirectory()) {
try {
templateEngine.addPath(mergeDir.getPath());
} catch (Exception e) {
throw new Error(e);
}
}
}
public String getSetterName(BeanProperty prop) {
StringBuffer retBuf = new StringBuffer(prop.getName());
retBuf.setCharAt(0, Character.toUpperCase(retBuf.charAt(0)));
retBuf.insert(0, "set");
return retBuf.toString();
}
public String getGetterName(BeanProperty prop) {
StringBuffer retBuf = new StringBuffer(prop.getName());
retBuf.setCharAt(0, Character.toUpperCase(retBuf.charAt(0)));
if (prop.getType().isA(new Type("boolean")) || prop.getType().isA(new Type("java.lang.Boolean"))) {
retBuf.insert(0, "is");
} else {
retBuf.insert(0, "get");
}
return retBuf.toString();
}
/**
* Setter for mergeDir property
*
* @param mergeDir The value for the property
*/
public void setMergedir(File mergeDir) {
this.mergeDir = mergeDir;
}
/**
* Utility method called from script to resolve a mergeFile reference
*
* @param mergeFile The mergeFile to look for
*
* @return A File for mergeFile
*/
public File getMergeFile(String mergeFile) {
if ((mergeFile != null) && (mergeDir != null) && mergeDir.isDirectory()) {
File retVal = new File(mergeDir, mergeFile);
return isIn(retVal, mergeDir) ? retVal : null;
}
// if ((mergeFile != null) && (mergeDir != null) && mergeDir.isDirectory()) {
// // The listing of mergeDir's files avoid possibly security issues in path resolving (paranoid?)
// File[] files = mergeDir.listFiles();
//
// for (int i = 0; i < files.length; i++) {
// if (mergeFile.trim().equals(files[i].getName())) {
// return files[i];
// }
// }
// }
return null;
}
public String getPad(int depth) {
StringBuffer retBuf = new StringBuffer();
while (depth-- > 1) {
retBuf.append(" ");
}
return retBuf.toString();
}
/**
* Returns the force-flag.
*/
public boolean isForce() {
return force;
}
/**
* Set the force flag. When false, the plugin will only generate target
* files if the source file is newer.
*
* @param force
*/
public void setForce(boolean force) {
this.force = force;
}
/**
* Returns whether or not this plugin is being ran in verbose mode.
*/
public boolean isVerbose() {
return verbose;
}
/**
* Sets the verbose flag. When verbose = true, the plugin will explain
* what it's doing, otherwise it will run silently.
*/
public void setVerbose(boolean verbose) {
this.verbose = verbose;
}
/**
* Returns true if the source file has been changed after the target file
* has been generated.
*
* @param javaClass
* @return
*/
protected boolean isDestinationDirty(JavaClass javaClass) {
if (force) return true;
File destFile = getDestinationFile(javaClass);
File sourceFile = getSourceFile(javaClass);
return !(destFile.exists() &&
sourceFile.lastModified() < destFile.lastModified());
}
/**
* Returns a File object representing the Source File represented
* by the given JavaClass
* @param javaClass The source JavaClass
* @return A File Object representing the source file.
*/
protected File getSourceFile(JavaClass javaClass) {
return new File(javaClass.getSource().getURL().getFile());
}
/**
* Returns a File object representing the destination File represented
* by the given JavaClass
*
* @param javaClass The JavaClass for which to find the destination.
* @return A File Object representing the destination file.
*/
protected File getDestinationFile(JavaClass javaClass) {
String packagePath = getDestinationPackage(javaClass).replace('.', '/');
File dir = new File(getDestdirFile(), packagePath);
String filename = getDestinationFilename(javaClass);
return new File(dir, filename);
}
protected boolean isDynamicJavaClass(JavaClass clazz) {
return clazz instanceof DynamicJavaClass;
}
protected JavaClass createDynamicJavaClass(String className, String packaze, String[] imports, QDoxCapableMetadataProvider metadataProvider) {
JavaClass retVal = new DynamicJavaClass(className);
JavaDocBuilder builder = new JavaDocBuilder(metadataProvider.getDocletTagFactory());
retVal.setParent(createJavaClassParent(builder.getClassLibrary(), packaze, imports));
retVal.setJavaClassCache(createJavaClassCache(builder, metadataProvider.getMetadata(), retVal));
return retVal;
}
protected JavaClassParent createJavaClassParent(ClassLibrary classLibrary, String packaze, String[] importz) {
JavaSource src = new JavaSource();
src.setClassLibrary(classLibrary);
for (int i = 0; importz != null && i < importz.length; i++) {
src.addImport(importz[i]);
}
src.setPackage(packaze);
return src;
}
protected JavaClassCache createJavaClassCache(final JavaClassCache classCache, Collection metadata, final JavaClass clz) {
final Set javaClasses = new HashSet();
javaClasses.addAll(metadata);
javaClasses.add(clz);
return new JavaClassCache() {
public JavaClass[] getClasses() {
log.trace("createJavaClassCache.getClasses()");
return (JavaClass[])javaClasses.toArray(new JavaClass[0]);
}
public JavaClass getClassByName(final String name) {
if (log.isTraceEnabled()) {
log.trace("createJavaClassCache.getClassByName() name="+name);
}
if (name == null) {
return null;
}
for (Iterator iter = javaClasses.iterator(); iter.hasNext();) {
JavaClass javaClass = (JavaClass)iter.next();
if (javaClass.getFullyQualifiedName().equals(name)) {
if (log.isTraceEnabled()) {
log.trace("createJavaClassCache.getClassByName() FOUND javaClass="+javaClass);
}
return javaClass;
}
}
if (log.isTraceEnabled()) {
log.trace("createJavaClassCache.getClassByName() NOT FOUND! Using builder.getClassByName("+name+")");
}
// not found. Try to use JavaClassCache
return classCache.getClassByName(name);
}
};
}
private static class DynamicJavaClass extends JavaClass {
public DynamicJavaClass() {
super();
}
public DynamicJavaClass(String name) {
super(name);
}
}
}