blob: dc278cc909ecf1675a12b9ae262e05cbcd87a9f3 [file] [log] [blame]
/* Copyright 2004 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.apache.xmlbeans.impl.jam.internal;
import org.apache.xmlbeans.impl.jam.JamClassLoader;
import org.apache.xmlbeans.impl.jam.JamServiceParams;
import org.apache.xmlbeans.impl.jam.annotation.AnnotationProxy;
import org.apache.xmlbeans.impl.jam.annotation.DefaultAnnotationProxy;
import org.apache.xmlbeans.impl.jam.annotation.JavadocTagParser;
import org.apache.xmlbeans.impl.jam.annotation.WhitespaceDelimitedTagParser;
import org.apache.xmlbeans.impl.jam.internal.elements.ClassImpl;
import org.apache.xmlbeans.impl.jam.internal.elements.ElementContext;
import org.apache.xmlbeans.impl.jam.visitor.*;
import org.apache.xmlbeans.impl.jam.provider.JamServiceContext;
import org.apache.xmlbeans.impl.jam.provider.ResourcePath;
import org.apache.xmlbeans.impl.jam.provider.JamLogger;
import org.apache.xmlbeans.impl.jam.provider.JamClassBuilder;
import org.apache.xmlbeans.impl.jam.provider.CompositeJamClassBuilder;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.*;
/**
* <p>Takes settings from the user (through JamServiceParams) and exposes
* them to the implementation (through JamServiceContext).</p>
*
* @author Patrick Calahan &lt;email: pcal-at-bea-dot-com&gt;
*/
public class JamServiceContextImpl extends JamLoggerImpl implements JamServiceContext,
JamServiceParams, ElementContext
{
// ========================================================================
// Variables
private Class mDefaultAnnotationProxyClass = null;
private Properties mProperties = null;
private Map mSourceRoot2Scanner = null;
private Map mClassRoot2Scanner = null;
private Map mAnnname2proxyclass = null;
private List mClasspath = null;
private List mSourcepath = null;
private List mToolClasspath = null;
private List mIncludeClasses = null;
private List mExcludeClasses = null;
private boolean mUseSystemClasspath = true;
private JavadocTagParser mTagParser = null;
private MVisitor mCommentInitializer = null;
private MVisitor mPropertyInitializer = new PropertyInitializer();
private List mOtherInitializers = null;
private List mUnstructuredSourceFiles = null;
private List mClassLoaders = null;
private List mBaseBuilders = null;
private JamClassLoader mLoader = null;
// ========================================================================
// REVIEW
public void setClassLoader(JamClassLoader loader) {
mLoader = loader;
}
//may want to expose this in JamServiceParams
public void addBaseBuilder(JamClassBuilder builder) {
if (mBaseBuilders == null) mBaseBuilders = new ArrayList();
mBaseBuilders.add(builder);
}
public JamClassBuilder getBaseBuilder() {
if (mBaseBuilders == null || mBaseBuilders.size() == 0) {
return null;
}
if (mBaseBuilders.size() == 1) {
return (JamClassBuilder)mBaseBuilders.get(0);
}
JamClassBuilder[] comp = new JamClassBuilder[mBaseBuilders.size()];
mBaseBuilders.toArray(comp);
return new CompositeJamClassBuilder(comp);
}
public JavadocTagParser getTagParser() {
if (mTagParser == null) {
mTagParser = new WhitespaceDelimitedTagParser();
mTagParser.init(this);
}
return mTagParser;
}
// ========================================================================
// Constructors
public JamServiceContextImpl() {}
// ========================================================================
// Public methods - used by BaseJProvider
/**
* Returns an array containing the qualified names of the classes which
* are in the Service class set.
*/
public String[] getAllClassnames() throws IOException {
Set all = new HashSet();
if (mIncludeClasses != null) all.addAll(mIncludeClasses);
for(Iterator i = getAllDirectoryScanners(); i.hasNext(); ) {
DirectoryScanner ds = (DirectoryScanner)i.next();
String[] files = ds.getIncludedFiles();
for(int j=0; j<files.length; j++) {
all.add(filename2classname(files[j]));
}
}
if (mExcludeClasses != null) all.removeAll(mExcludeClasses);
String[] out = new String[all.size()];
all.toArray(out);
return out;
}
public JamLogger getLogger() { return this; }
/*
public String[] getSourceClassnames() throws IOException {
if (mSourceRoot2Scanner == null) return new String[0];
Set set = new HashSet();
for(Iterator i = mSourceRoot2Scanner.values().iterator(); i.hasNext(); ) {
DirectoryScanner ds = (DirectoryScanner)i.next();
String[] files = ds.getIncludedFiles();
for(int j=0; j<files.length; j++) {
set.add(filename2classname(files[j]));
}
}
String[] out = new String[set.size()];
set.toArray(out);
return out;
}*/
public File[] getSourceFiles() throws IOException {
Set set = new HashSet();
if (mSourceRoot2Scanner != null) {
for(Iterator i = mSourceRoot2Scanner.values().iterator(); i.hasNext(); ) {
DirectoryScanner ds = (DirectoryScanner)i.next();
if (isVerbose(this)) {
verbose(PREFIX+ " checking scanner for dir"+ds.getRoot());
}
String[] files = ds.getIncludedFiles();
for(int j=0; j<files.length; j++) {
if (isVerbose(this)) {
verbose(PREFIX+ " ...including a source file "+files[j]);
}
set.add(new File(ds.getRoot(),files[j]));
}
}
}
// also dump unstructured files in there as well. javadoc doesn't
// know the difference, but eventually we're going to care
// when we introduce lazy parsing
if (mUnstructuredSourceFiles != null) {
if (isVerbose(this)) verbose(PREFIX+ "adding "+mUnstructuredSourceFiles.size()+
" other source files");
set.addAll(mUnstructuredSourceFiles);
}
File[] out = new File[set.size()];
set.toArray(out);
return out;
}
public File[] getUnstructuredSourceFiles() {
if (mUnstructuredSourceFiles == null) return null;
File[] out = new File[mUnstructuredSourceFiles.size()];
mUnstructuredSourceFiles.toArray(out);
return out;
}
public ResourcePath getInputClasspath() { return createJPath(mClasspath); }
public ResourcePath getInputSourcepath() { return createJPath(mSourcepath); }
public ResourcePath getToolClasspath() { return createJPath(mToolClasspath); }
public String getProperty(String name) {
return (mProperties == null) ? null : mProperties.getProperty(name);
}
public void registerAnnotationProxy(Class proxy, String annotationName) {
validateProxyClass(proxy);
ClassImpl.validateClassName(annotationName);
if (mAnnname2proxyclass == null) {
mAnnname2proxyclass = new HashMap();
} else {
Class current = (Class)mAnnname2proxyclass.get(annotationName);
if (current != null) {
throw new IllegalArgumentException("A proxy is already registered for "
+annotationName+": "+current.getName());
}
}
mAnnname2proxyclass.put(annotationName,proxy);
}
public MVisitor getInitializer() {
List initers = new ArrayList();
// for now, we don't have a default comment initializer. may need to
// change this someday.
if (mCommentInitializer != null) initers.add(mCommentInitializer);
// initers.add((mCommentInitializer != null) ? mCommentInitializer :
// new CommentInitializer());
if (mPropertyInitializer != null) initers.add(mPropertyInitializer);
if (mOtherInitializers != null) initers.addAll(mOtherInitializers);
// now go
MVisitor[] inits = new MVisitor[initers.size()];
initers.toArray(inits);
return new CompositeMVisitor(inits);
}
// ========================================================================
// JamServiceParams implementation
//DOCME
public void setCommentInitializer(MVisitor initializer) {
mCommentInitializer = initializer;
}
//DOCME
public void setPropertyInitializer(MVisitor initializer) {
mPropertyInitializer = initializer;
}
//DOCME
public void addInitializer(MVisitor initializer) {
if (mOtherInitializers == null) mOtherInitializers = new ArrayList();
mOtherInitializers.add(initializer);
}
//DOCME
public void setJavadocTagParser(JavadocTagParser tp) {
mTagParser = tp;
tp.init(this); //FIXME this is a little broken to do this here
}
public void includeSourceFile(File file) {
if (file == null) throw new IllegalArgumentException("null file");
if (isVerbose(this)) verbose(PREFIX+ "adding source "+file.getAbsoluteFile());
if (mUnstructuredSourceFiles == null) {
mUnstructuredSourceFiles = new ArrayList();
}
mUnstructuredSourceFiles.add(file.getAbsoluteFile());
}
public void includeSourcePattern(File[] sourcepath, String pattern) {
if (sourcepath == null) throw new IllegalArgumentException("null sourcepath");
if (sourcepath.length == 0) throw new IllegalArgumentException("empty sourcepath");
if (pattern == null) throw new IllegalArgumentException("null pattern");
pattern = pattern.trim();
if (pattern.length() == 0) throw new IllegalArgumentException("empty pattern");
for(int i=0; i<sourcepath.length; i++) {
if (isVerbose(this)) verbose(PREFIX+ "including '"+pattern+"' under "+sourcepath[i]);
addSourcepath(sourcepath[i]);
getSourceScanner(sourcepath[i]).include(pattern);
}
}
public void includeClassPattern(File classpath[], String pattern) {
if (classpath == null) throw new IllegalArgumentException("null classpath");
if (classpath.length == 0) throw new IllegalArgumentException("empty classpath");
if (pattern == null) throw new IllegalArgumentException("null pattern");
pattern = pattern.trim();
if (pattern.length() == 0) throw new IllegalArgumentException("empty pattern");
for(int i=0; i<classpath.length; i++) {
if (isVerbose(this)) verbose(PREFIX+ "including '"+pattern+"' under "+classpath[i]);
addClasspath(classpath[i]);
getClassScanner(classpath[i]).include(pattern);
}
}
public void excludeSourcePattern(File[] sourcepath, String pattern) {
if (sourcepath == null) throw new IllegalArgumentException("null sourcepath");
if (sourcepath.length == 0) throw new IllegalArgumentException("empty sourcepath");
if (pattern == null) throw new IllegalArgumentException("null pattern");
pattern = pattern.trim();
if (pattern.length() == 0) throw new IllegalArgumentException("empty pattern");
for(int i=0; i<sourcepath.length; i++) {
if (isVerbose(this)) verbose(PREFIX+ "EXCLUDING '"+pattern+"' under "+sourcepath[i]);
addSourcepath(sourcepath[i]);
getSourceScanner(sourcepath[i]).exclude(pattern);
}
}
public void excludeClassPattern(File[] classpath, String pattern) {
if (classpath == null) throw new IllegalArgumentException("null classpath");
if (classpath.length == 0) throw new IllegalArgumentException("empty classpath");
if (pattern == null) throw new IllegalArgumentException("null pattern");
pattern = pattern.trim();
if (pattern.length() == 0) throw new IllegalArgumentException("empty pattern");
for(int i=0; i<classpath.length; i++) {
if (isVerbose(this)) verbose(PREFIX+ "EXCLUDING '"+pattern+"' under "+classpath[i]);
addClasspath(classpath[i]);
getClassScanner(classpath[i]).exclude(pattern);
}
}
public void includeSourceFile(File[] sourcepath, File sourceFile) {
File root = getPathRootForFile(sourcepath,sourceFile);
includeSourcePattern(new File[] {root}, source2pattern(root,sourceFile));
}
public void excludeSourceFile(File[] sourcepath, File sourceFile) {
File root = getPathRootForFile(sourcepath,sourceFile);
excludeSourcePattern(new File[] {root}, source2pattern(root,sourceFile));
}
public void includeClassFile(File[] classpath, File classFile) {
File root = getPathRootForFile(classpath,classFile);
includeClassPattern(new File[] {root}, source2pattern(root,classFile));
}
public void excludeClassFile(File[] classpath, File classFile) {
File root = getPathRootForFile(classpath,classFile);
excludeClassPattern(new File[] {root}, source2pattern(root,classFile));
}
public void includeClass(String qualifiedClassname) {
if (mIncludeClasses == null) mIncludeClasses = new ArrayList();
mIncludeClasses.add(qualifiedClassname);
}
public void excludeClass(String qualifiedClassname) {
if (mExcludeClasses == null) mExcludeClasses = new ArrayList();
mExcludeClasses.add(qualifiedClassname);
}
public void addClasspath(File classpathElement) {
if (mClasspath == null) {
mClasspath = new ArrayList();
} else {
if (mClasspath.contains(classpathElement)) return;
}
mClasspath.add(classpathElement);
}
public void setLoggerWriter(PrintWriter out) {
super.setOut(out);//FIXME
}
public void addSourcepath(File sourcepathElement) {
if (mSourcepath == null) {
mSourcepath = new ArrayList();
} else {
if (mSourcepath.contains(sourcepathElement)) return;
}
mSourcepath.add(sourcepathElement);
}
public void addToolClasspath(File classpathElement) {
if (mToolClasspath == null) {
mToolClasspath = new ArrayList();
} else {
if (mToolClasspath.contains(classpathElement)) return;
}
mToolClasspath.add(classpathElement);
}
public void setProperty(String name, String value) {
if (mProperties == null) mProperties = new Properties();
mProperties.setProperty(name,value);
}
//public void setLogger(PrintWriter out) { mOut = out; }
public void setParentClassLoader(JamClassLoader loader) {
throw new IllegalStateException("NYI"); //FIXME
}
public void setUseSystemClasspath(boolean use) {
mUseSystemClasspath = use;
}
public void addClassLoader(ClassLoader cl) {
if (mClassLoaders == null) mClassLoaders = new ArrayList();
mClassLoaders.add(cl);
}
public void setDefaultAnnotationProxyClass(Class proxy) {
validateProxyClass(proxy);
mDefaultAnnotationProxyClass = proxy;
}
// ========================================================================
// JamServiceContext implementation
//public boolean isUseSystemClasspath() { return mUseSystemClasspath; }
public ClassLoader[] getReflectionClassLoaders() {
if (mClassLoaders == null) {
if (mUseSystemClasspath) {
return new ClassLoader[] { ClassLoader.getSystemClassLoader() };
} else {
return new ClassLoader[0];
}
} else {
ClassLoader[] out = new ClassLoader[mClassLoaders.size()+
(mUseSystemClasspath ? 1 : 0)];
for(int i=0; i<mClassLoaders.size(); i++) {
out[i] = (ClassLoader)mClassLoaders.get(i);
}
if (mUseSystemClasspath) {
out[out.length-1] = ClassLoader.getSystemClassLoader();
}
return out;
}
}
// ========================================================================
// ElementContext implementation
public JamClassLoader getClassLoader() { return mLoader; }
public AnnotationProxy createAnnotationProxy(String jsr175typename) {
Class pc = null;
if (mAnnname2proxyclass != null) {
pc = (Class)mAnnname2proxyclass.get(jsr175typename);
if (pc == null) pc = DefaultAnnotationProxy.class;
} else {
pc = DefaultAnnotationProxy.class;
}
return createProxy(pc);
}
// ========================================================================
// Static utilities
/**
* <p>Checks to make sure the given class is an acceptable subclass of
* AnnotationProxy, throws an IllegalArgumentException if not.</p>
*/
public static void validateProxyClass(Class proxy) {
if (proxy == null) throw new IllegalArgumentException("null proxy class");
if (!AnnotationProxy.class.isAssignableFrom(proxy)) {
throw new IllegalArgumentException(proxy.getName()+
" does not extend from "+AnnotationProxy.class.getName());
}
if (!Modifier.isPublic(proxy.getModifiers())) {
throw new IllegalArgumentException(proxy.getName()+" is not public");
}
if (Modifier.isAbstract(proxy.getModifiers())) {
throw new IllegalArgumentException(proxy.getName()+" is abstract.");
}
try {
Constructor dfltCtor = proxy.getConstructor(new Class[0]);
if (!Modifier.isPublic(dfltCtor.getModifiers())) {
throw new IllegalArgumentException("The default constructor on"+
proxy.getName()+" is not public.");
}
} catch(NoSuchMethodException nsme) {
throw new IllegalArgumentException(proxy.getName()+
" does not have a default constructor");
}
}
// ========================================================================
// Private methods
private static final String PREFIX = "[JamServiceContextImpl] ";
private File getPathRootForFile(File[] sourcepath, File sourceFile) {
if (sourcepath == null) throw new IllegalArgumentException("null sourcepath");
if (sourcepath.length == 0) throw new IllegalArgumentException("empty sourcepath");
if (sourceFile == null) throw new IllegalArgumentException("null sourceFile");
sourceFile = sourceFile.getAbsoluteFile();
if (isVerbose(this)) verbose(PREFIX+"Getting root for "+sourceFile+"...");
for(int i=0; i<sourcepath.length; i++) {
if (isVerbose(this)) verbose(PREFIX+"...looking in "+sourcepath[i]);
if (isContainingDir(sourcepath[i].getAbsoluteFile(),sourceFile)) {
if (isVerbose(this)) verbose(PREFIX+"...found it!");
return sourcepath[i].getAbsoluteFile();
}
}
throw new IllegalArgumentException(sourceFile+" is not in the given path.");
}
/**
* Returns true if the given dir contains the given file.
*/
private boolean isContainingDir(File dir, File file) {
if (isVerbose(this)) verbose(PREFIX+ "... ...isContainingDir "+dir+" "+file);
if (file == null) return false;
if (dir.equals(file)) {
if (isVerbose(this)) verbose(PREFIX+ "... ...yes!");
return true;
}
return isContainingDir(dir,file.getParentFile());
}
private AnnotationProxy createProxy(Class clazz) {
if (clazz == null) clazz = mDefaultAnnotationProxyClass;
AnnotationProxy p;
if (clazz != null) {
try {
//hopefully, it's pretty unlikely anything will go wrong, since
//we validate all proxy classes on the way in
p = (AnnotationProxy)clazz.newInstance();
p.init(this);
return p;
} catch (IllegalAccessException iae) {
error(iae);
} catch (ClassCastException cce) {
error(cce);
} catch (InstantiationException ie) {
error(ie);
}
}
p = new DefaultAnnotationProxy();
p.init(this);
return p;
}
/**
* Converts the sourceFile to a pattern expression relative to the
* given root.
*/
private String source2pattern(File root, File sourceFile) {
if (isVerbose(this)) verbose(PREFIX+ "source2pattern "+root+" "+sourceFile);
//REVIEW this is a bit cheesy
String r = root.getAbsolutePath();
String s = sourceFile.getAbsolutePath();
if (isVerbose(this)) {
verbose(PREFIX+ "source2pattern returning "+s.substring(r.length()+1));
}
return s.substring(r.length()+1);
}
/**
* Converts the given java source or class filename into a qualified
* classname. The filename is assumed to be relative to the source or
* class root.
*/
private static String filename2classname(String filename) {
int extDot = filename.lastIndexOf('.');
if (extDot != -1) filename = filename.substring(0,extDot);
filename = filename.replace('/','.');
filename = filename.replace('\\','.');
return filename;
}
/**
* Returns all of the directory scanners for all class and source
* roots created in this params object.
*/
private Iterator getAllDirectoryScanners() {
Collection out = new ArrayList();
if (mSourceRoot2Scanner != null) {
out.addAll(mSourceRoot2Scanner.values());
}
if (mClassRoot2Scanner != null) {
out.addAll(mClassRoot2Scanner.values());
}
return out.iterator();
}
/**
* Creates a ResourcePath for the given collection of Files, or returns null
* if the collections is null or empty.
*/
private static ResourcePath createJPath(Collection filelist) {
if (filelist == null || filelist.size() == 0) return null;
File[] files = new File[filelist.size()];
filelist.toArray(files);
return ResourcePath.forFiles(files);
}
/**
* Returns the DirectoryScanner which we have mapped to the given source
* root, creating a new one if necessary.
*/
private DirectoryScanner getSourceScanner(File srcRoot) {
if (mSourceRoot2Scanner == null) mSourceRoot2Scanner = new HashMap();
DirectoryScanner out = (DirectoryScanner)mSourceRoot2Scanner.get(srcRoot);
if (out == null) {
mSourceRoot2Scanner.put(srcRoot,out = new DirectoryScanner(srcRoot,this));
}
return out;
}
/**
* Returns the DirectoryScanner which we have mapped to the given class
* root, creating a new one if necessary.
*/
private DirectoryScanner getClassScanner(File clsRoot) {
if (mClassRoot2Scanner == null) mClassRoot2Scanner = new HashMap();
DirectoryScanner out = (DirectoryScanner)mClassRoot2Scanner.get(clsRoot);
if (out == null) {
mClassRoot2Scanner.put(clsRoot,out = new DirectoryScanner(clsRoot,this));
}
return out;
}
}