diff options
Diffstat (limited to 'src/main/java/net/sf/antcontrib/design')
9 files changed, 1722 insertions, 0 deletions
diff --git a/src/main/java/net/sf/antcontrib/design/Depends.java b/src/main/java/net/sf/antcontrib/design/Depends.java new file mode 100644 index 0000000..570efc0 --- /dev/null +++ b/src/main/java/net/sf/antcontrib/design/Depends.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2001-2004 Ant-Contrib project. All rights reserved. + * + * 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. + */ +/* + * Created on Dec 24, 2004 + */ +package net.sf.antcontrib.design; + +/** + * + * @author dhiller + */ +public class Depends { + + private String name; + + public Depends() {} + /** + * @param name + */ + public Depends(String name) { + super(); + this.name = name; + } + /** + * @param string + */ + public void setName(String s) { + this.name = s; + } + + public String getName() { + return name; + } + + public String toString() { + return name; + } +} diff --git a/src/main/java/net/sf/antcontrib/design/Design.java b/src/main/java/net/sf/antcontrib/design/Design.java new file mode 100644 index 0000000..c88db1c --- /dev/null +++ b/src/main/java/net/sf/antcontrib/design/Design.java @@ -0,0 +1,330 @@ +/*
+ * Copyright (c) 2001-2005 Ant-Contrib project. All rights reserved.
+ *
+ * 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 net.sf.antcontrib.design;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.Vector;
+
+import net.sf.antcontrib.logic.ProjectDelegate;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Location;
+import org.apache.tools.ant.Project;
+
+
+/*
+ * Created on Aug 24, 2003
+ *
+ * To change the template for this generated file go to
+ * Window>Preferences>Java>Code Generation>Code and Comments
+ */
+/**
+ * FILL IN JAVADOC HERE
+ *
+ * @author Dean Hiller([email protected])
+ */
+public class Design {
+
+ private Map nameToPackage = new HashMap();
+ private Map packageNameToPackage = new HashMap();
+ private boolean isCircularDesign;
+ private Log log;
+ private Location location;
+
+ private String currentClass = null;
+ private String currentPackageName = null;
+ private Package currentAliasPackage = null;
+
+ private HashSet primitives = new HashSet();
+
+ public Design(boolean isCircularDesign, Log log, Location loc) {
+ //by default, add java as a configured package with the name java
+ Package p = new Package();
+ p.setIncludeSubpackages(true);
+ p.setName("java");
+ p.setUsed(true);
+ p.setNeedDeclarations(false);
+ p.setPackage("java");
+ addConfiguredPackage(p);
+
+ this.isCircularDesign = isCircularDesign;
+ this.log = log;
+ this.location = loc;
+
+ primitives.add("boolean");
+
+ //integral types
+ primitives.add("byte");
+ primitives.add("short");
+ primitives.add("int");
+ primitives.add("long");
+ primitives.add("char");
+
+ //floating point types
+ primitives.add("double");
+ primitives.add("float");
+ }
+
+ public Package getPackage(String nameAttribute) {
+ return (Package)nameToPackage.get(nameAttribute);
+ }
+
+ private Package retreivePack(String thePackage) {
+ if(thePackage == null)
+ throw new IllegalArgumentException("Cannot retrieve null packages");
+
+ String currentPackage = thePackage;
+ Package result = (Package)packageNameToPackage.get(currentPackage);
+ while(!Package.DEFAULT.equals(currentPackage)) {
+ log.log("p="+currentPackage+"result="+result, Project.MSG_DEBUG);
+ if(result != null) {
+ if(currentPackage.equals(thePackage))
+ return result;
+ else if(result.isIncludeSubpackages())
+ return result;
+ return null;
+ }
+ currentPackage = VerifyDesignDelegate.getPackageName(currentPackage);
+ result = (Package)packageNameToPackage.get(currentPackage);
+ }
+
+ //result must now be default package
+ if(result != null && result.isIncludeSubpackages())
+ return result;
+
+ return null;
+ }
+
+ public void addConfiguredPackage(Package p) {
+
+ String pack = p.getPackage();
+
+ Depends[] depends = p.getDepends();
+
+ if(depends != null && !isCircularDesign) {
+ //make sure all depends are in Map first
+ //circular references then are not a problem because they must
+ //put the stuff in order
+ for(int i = 0; i < depends.length; i++) {
+ Package dependsPackage = (Package)nameToPackage.get(depends[i].getName());
+
+ if(dependsPackage == null) {
+ throw new RuntimeException("package name="+p.getName()+" did not\n" +
+ "have "+depends[i]+" listed before it. circularDesign is off\n"+
+ "so package="+p.getName()+" must be moved up in the xml file");
+ }
+ }
+ }
+
+ nameToPackage.put(p.getName(), p);
+ packageNameToPackage.put(p.getPackage(), p);
+ }
+
+ /**
+ * @param className Class name of a class our currentAliasPackage depends on.
+ */
+ public void verifyDependencyOk(String className) {
+ log.log(" className="+className, Project.MSG_DEBUG);
+ if(className.startsWith("L"))
+ className = className.substring(1, className.length());
+
+ //get the classPackage our currentAliasPackage depends on....
+ String classPackage = VerifyDesignDelegate.getPackageName(className);
+
+ //check if this is an needdeclarations="false" package, if so, the dependency is ok if it
+ //is not declared
+ log.log(" classPackage="+classPackage, Project.MSG_DEBUG);
+ Package p = retreivePack(classPackage);
+ if(p == null) {
+ throw new BuildException(getErrorMessage(currentClass, className), location);
+ }
+ p.setUsed(true); //set package to used since we have classes in it
+ if(p != null && !p.isNeedDeclarations())
+ return;
+
+ String pack = currentAliasPackage.getPackage();
+
+ log.log(" AllowedDepends="+pack, Project.MSG_DEBUG);
+ log.log(" CurrentDepends="+className, Project.MSG_DEBUG);
+ if(isClassInPackage(className, currentAliasPackage))
+ return;
+
+ Depends[] depends = currentAliasPackage.getDepends();
+
+ //probably want to create a regular expression out of all the depends and just match on that
+ //each time. for now though, just get it working and do the basic(optimize later if needed)
+ for(int i = 0; i < depends.length; i++) {
+ Depends d = depends[i];
+ String name = d.getName();
+
+ Package temp = getPackage(name);
+ log.log(" AllowedDepends="+temp.getPackage(), Project.MSG_DEBUG);
+ log.log(" CurrentDepends="+className, Project.MSG_DEBUG);
+ if(isClassInPackage(className, temp)) {
+ temp.setUsed(true); //set package to used since we are depending on it(could be external package like junit)
+ currentAliasPackage.addUsedDependency(d);
+ return;
+ }
+ }
+
+ log.log("***************************************", Project.MSG_DEBUG);
+ log.log("***************************************", Project.MSG_DEBUG);
+
+ throw new BuildException(Design.getErrorMessage(currentClass, className), location);
+ }
+
+ public boolean isClassInPackage(String className, Package p) {
+ String classPackage = VerifyDesignDelegate.getPackageName(className);
+ if(p.isIncludeSubpackages()) {
+ if(className.startsWith(p.getPackage()))
+ return true;
+ } else { //if not including subpackages, the it must be the exact package.
+ if(classPackage.equals(p.getPackage()))
+ return true;
+ }
+ return false;
+ }
+ /**
+ * @param className
+ * @return whether or not this class needs to be checked. (ie. if the
+ * attribute needdepends=false, we don't care about this package.
+ */
+ public boolean needEvalCurrentClass(String className) {
+ currentClass = className;
+ String packageName = VerifyDesignDelegate.getPackageName(className);
+// log("class="+className, Project.MSG_DEBUG);
+ if(!packageName.equals(currentPackageName) || currentAliasPackage == null) {
+ currentPackageName = packageName;
+ log.log("\nEvaluating package="+currentPackageName, Project.MSG_INFO);
+ currentAliasPackage = retreivePack(packageName);
+ //DEANDO: test this scenario
+ if(currentAliasPackage == null) {
+ log.log(" class="+className, Project.MSG_VERBOSE);
+ throw new BuildException(getNoDefinitionError(className), location);
+ }
+
+ currentAliasPackage.setUsed(true);
+ }
+ log.log(" class="+className, Project.MSG_VERBOSE);
+
+ if(packageName.equals(Package.DEFAULT)) {
+ if(className.indexOf('.') != -1) {
+ throw new RuntimeException("Internal Error");
+ }
+ } else if(!className.startsWith(currentPackageName))
+ throw new RuntimeException("Internal Error");
+
+ if(!currentAliasPackage.getNeedDepends())
+ return false;
+ return true;
+ }
+
+ public String getCurrentClass() {
+ return currentClass;
+ }
+
+ void checkClass(String dependsOn) {
+ log.log(" dependsOn1="+dependsOn, Project.MSG_DEBUG);
+ if(dependsOn.endsWith("[]")) {
+ int index = dependsOn.indexOf("[");
+ dependsOn = dependsOn.substring(0, index);
+ log.log(" dependsOn2="+dependsOn, Project.MSG_DEBUG);
+ }
+
+ if(primitives.contains(dependsOn))
+ return;
+
+ //Anything in java.lang package seems to be passed in as just the
+ //className with no package like Object, String or Class, so here we try to
+ //see if the name is a java.lang class....
+ String tempTry = "java.lang."+dependsOn;
+ try {
+ Class c = VerifyDesign.class.getClassLoader().loadClass(tempTry);
+ return;
+ } catch(ClassNotFoundException e) {
+ //not found, continue on...
+ }
+ //sometimes instead of passing java.lang.String or java.lang.Object, the bcel
+ //passes just String or Object
+// if("String".equals(dependsOn) || "Object".equals(dependsOn))
+// return;
+
+ verifyDependencyOk(dependsOn);
+
+ }
+
+ public static String getErrorMessage(String className, String dependsOnClass) {
+ String s = "\nYou are violating your own design...." +
+ "\nClass = "+className+" depends on\nClass = "+dependsOnClass+
+ "\nThe dependency to allow this is not defined in your design" +
+ "\nPackage="+VerifyDesignDelegate.getPackageName(className)+" is not defined to depend on"+
+ "\nPackage="+VerifyDesignDelegate.getPackageName(dependsOnClass)+
+ "\nChange the code or the design";
+ return s;
+ }
+
+ public static String getNoDefinitionError(String className) {
+ String s = "\nPackage="+VerifyDesignDelegate.getPackageName(className)+" is not defined in the design.\n"+
+ "All packages with classes must be declared in the design file\n"+
+ "Class found in the offending package="+className;
+ return s;
+ }
+
+ public static String getWrapperMsg(File originalFile, String message) {
+ String s = "\nThe file '" + originalFile.getAbsolutePath() + "' failed due to: " + message;
+ return s;
+ }
+
+ /**
+ * @param designErrors
+ */
+ public void fillInUnusedPackages(Vector designErrors)
+ {
+ Collection values = nameToPackage.values();
+ Iterator iterator = values.iterator();
+ while(iterator.hasNext()) {
+ Package pack = (Package)iterator.next();
+ if(!pack.isUsed()) {
+ String msg = "Package name="+pack.getName()+" is unused. Full package="+pack.getPackage();
+ log.log(msg, Project.MSG_ERR);
+ designErrors.add(new BuildException(msg));
+ } else {
+ fillInUnusedDepends(designErrors, pack);
+ }
+ }
+ }
+
+ /**
+ * @param designErrors
+ * @param pack
+ */
+ private void fillInUnusedDepends(Vector designErrors, Package pack)
+ {
+ Iterator iterator = pack.getUnusedDepends().iterator();
+ while(iterator.hasNext()) {
+ Depends depends = (Depends)iterator.next();
+ String msg = "Package name="+pack.getName()+" has a dependency declared that is not true anymore. Please erase the dependency <depends>"+depends.getName()+"</depends> from package="+pack.getName();
+ log.log(msg, Project.MSG_ERR);
+ designErrors.add(new BuildException(msg));
+ }
+ }
+}
diff --git a/src/main/java/net/sf/antcontrib/design/DesignFileHandler.java b/src/main/java/net/sf/antcontrib/design/DesignFileHandler.java new file mode 100644 index 0000000..52e1bf2 --- /dev/null +++ b/src/main/java/net/sf/antcontrib/design/DesignFileHandler.java @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2004-2005 Ant-Contrib project. All rights reserved. + * + * 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 net.sf.antcontrib.design; + +import java.io.File; +import java.util.Stack; + +import org.apache.tools.ant.Location; +import org.apache.tools.ant.Project; +import org.xml.sax.Attributes; +import org.xml.sax.ContentHandler; +import org.xml.sax.InputSource; +import org.xml.sax.Locator; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; + + +/** + * Handler for the root element. Its only child must be the "project" element. + */ +class DesignFileHandler implements ContentHandler { + + private final static String DESIGN = "design"; + private final static String PACKAGE = "package"; + private final static String DEPENDS = "depends"; + + private Log log = null; + private File file = null; + private boolean isCircularDesign; + private boolean needDeclarationsDefault = true; + private boolean needDependsDefault = true; + + private Design design = null; + private Package currentPackage = null; + private Stack stack = new Stack(); + private Locator locator = null; + private Location loc; + + /** + * @param CompileWithWalls + */ + DesignFileHandler(Log log, File file, boolean isCircularDesign, Location loc) { + this.log = log; + this.file = file; + this.isCircularDesign = isCircularDesign; + this.loc = loc; + } + + /** + * @param needDeclarationsDefault + */ + public void setNeedDeclarationsDefault(boolean b) { + needDeclarationsDefault = b; + } + + /** + * @param needDependsDefault + */ + public void setNeedDependsDefault(boolean b) { + needDependsDefault = b; + } + + public Design getDesign() { + return design; + } + + /** + * Resolves file: URIs relative to the build file. + * + * @param publicId The public identifer, or <code>null</code> + * if none is available. Ignored in this + * implementation. + * @param systemId The system identifier provided in the XML + * document. Will not be <code>null</code>. + */ + public InputSource resolveEntity(String publicId, + String systemId) { + log.log("publicId="+publicId+" systemId="+systemId, + Project.MSG_VERBOSE); + return null; + } + + /** + * Sets the locator in the project helper for future reference. + * + * @param locator The locator used by the parser. + * Will not be <code>null</code>. + */ + public void setDocumentLocator(Locator locator) { + this.locator = locator; + } + + /** + * @see org.xml.sax.ContentHandler#startPrefixMapping(java.lang.String, java.lang.String) + */ + public void startPrefixMapping(String prefix, String uri) throws SAXException { + } + + /** + * @see org.xml.sax.ContentHandler#endPrefixMapping(java.lang.String) + */ + public void endPrefixMapping(String prefix) throws SAXException { + } + + /** + * @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes) + */ + public void startElement(String uri, String name, String qName, Attributes attrs) throws SAXException { + log.log("Parsing startElement="+name, Project.MSG_DEBUG); + if (name == null || "".equals(name)) { + // XMLReader is not-namespace aware + name = qName; + } + + try { + Object o = null; + if(name.equals(DESIGN)) { + o = handleDesign(attrs); + } else if(name.equals(PACKAGE)) { + currentPackage = handlePackage(attrs); + o = currentPackage; + } else if(name.equals(DEPENDS)) { + o = handleDepends(attrs); + } else { + throw new SAXParseException("Error in file="+file.getAbsolutePath() + +", Unexpected element \"" + name + "\"", locator); + } + stack.push(o); + } catch(RuntimeException e) { + log.log("exception111111111111111111", Project.MSG_INFO); + throw new SAXParseException("PRoblem parsing", locator, e); + } + } + + private Design handleDesign(Attributes attrs) throws SAXParseException { + if(attrs.getLength() > 0) + throw new SAXParseException("Error in file="+file.getAbsolutePath() + +", no attributes allowed for "+DESIGN+" element", locator); + else if(stack.size() > 0) + throw new SAXParseException("Error in file="+file.getAbsolutePath() + +", "+DESIGN+" cannot be a subelement of "+stack.pop(), locator); + else if(attrs.getLength() > 0) + throw new SAXParseException("Error in file="+file.getAbsolutePath() + +", "+DESIGN+" element can't have any attributes", locator); + design = new Design(isCircularDesign, log, loc); + return design; + } + + private Package handlePackage(Attributes attrs) throws SAXParseException { + if(stack.size() <= 0 || !(stack.peek() instanceof Design)) + throw new SAXParseException("Error in file="+file.getAbsolutePath() + +", "+PACKAGE+" element must be nested in a "+DESIGN+" element", locator); + + int len = attrs.getLength(); + String name = null; + String thePackage = null; + String depends = null; + String subpackages = null; + String needDeclarations = null; + String needDepends = null; + for(int i = 0; i < len; i++) { + String attrName = attrs.getLocalName(i); + + if ("".equals(attrName)) { + // XMLReader is not-namespace aware + attrName = attrs.getQName(i); + } + String value = attrs.getValue(i); + log.log("attr="+attrName+" value="+value, Project.MSG_DEBUG); + if("name".equals(attrName)) + name = value; + else if("package".equals(attrName)) + thePackage = value; + else if("depends".equals(attrName)) + depends = value; + else if("subpackages".equals(attrName)) + subpackages = value; + else if("needdeclarations".equals(attrName)) + needDeclarations = value; + else if("needdepends".equals(attrName)) + needDepends = value; + else + throw new SAXParseException("Error in file="+file.getAbsolutePath() + +"\n'"+attrName+"' attribute is an invalid attribute for the package element", locator); + } + + //set the defaults + if(subpackages == null) + subpackages = "exclude"; + if(needDeclarations == null) + needDeclarations = Boolean.toString(needDeclarationsDefault); + if(needDepends == null) + needDepends = Boolean.toString(needDependsDefault); + + //make sure every attribute had a valid value... + if(name == null) + throw new SAXParseException("Error in file="+file.getAbsolutePath() + +", package element must contain the 'name' attribute", locator); + else if(thePackage == null) + throw new SAXParseException("Error in file="+file.getAbsolutePath() + +", package element must contain the 'package' attribute", locator); + else if(!("include".equals(subpackages) || "exclude".equals(subpackages))) + throw new SAXParseException("Error in file="+file.getAbsolutePath() + +"\nThe subpackages attribute in the package element can only have a" + +"\nvalue of \"include\" or \"exclude\". value='"+subpackages+"'", locator); + else if(!("true".equals(needDeclarations) || "false".equals(needDeclarations))) + throw new SAXParseException("Error in file="+file.getAbsolutePath() + +"\nThe needdeclarations attribute in the package element can only have a" + +"\nvalue of \"true\" or \"false\". value='"+needDeclarations+"'", locator); + else if(!("true".equals(needDepends) || "false".equals(needDepends))) + throw new SAXParseException("Error in file="+file.getAbsolutePath() + +"\nThe needdepends attribute in the package element can only have a" + +"\nvalue of \"true\" or \"false\". value='"+needDepends+"'", locator); + + Package p = new Package(); + p.setName(name); + p.setPackage(thePackage); + if("exclude".equals(subpackages)) + p.setIncludeSubpackages(false); + else + p.setIncludeSubpackages(true); + if("true".equals(needDeclarations)) + p.setNeedDeclarations(true); + else + p.setNeedDeclarations(false); + if("true".equals(needDepends)) + p.setNeedDepends(true); + else + p.setNeedDepends(false); + + if(depends != null) + p.addDepends(new Depends(depends)); + return p; + } + + private Depends handleDepends(Attributes attrs) throws SAXParseException { + if(stack.size() <= 0 || !(stack.peek() instanceof Package)) + throw new SAXParseException("Error in file="+file.getAbsolutePath() + +", "+DEPENDS+" element must be nested in a "+PACKAGE+" element", locator); + else if(attrs.getLength() > 0) + throw new SAXParseException("Error in file="+file.getAbsolutePath() + +", "+DEPENDS+" element can't have any attributes", locator); + + return new Depends(); + } + + /** + * @see org.xml.sax.ContentHandler#endElement(java.lang.String, java.lang.String, java.lang.String) + */ + public void endElement(String uri, String localName, String qName) throws SAXException { + try { + Object o = stack.pop(); + if(o instanceof Package) { + Package p = (Package)o; + + Package tmp = design.getPackage(p.getName()); + if(tmp != null) + throw new SAXParseException("Error in file="+file.getAbsolutePath() + +"\nname attribute on "+PACKAGE+" element has the same\n" + +"name as another package. name=\""+p.getName()+"\" is used twice or more", locator); + + + design.addConfiguredPackage(p); + currentPackage = null; + } else if(o instanceof Depends) { + Depends d = (Depends)o; + currentPackage.addDepends(d); + } + } catch(RuntimeException e) { + throw new SAXParseException("exception", locator, e); + } + } + + /** + * @see org.xml.sax.ContentHandler#skippedEntity(java.lang.String) + */ + public void skippedEntity(String name) throws SAXException { + } + + /** + * @see org.xml.sax.ContentHandler#startDocument() + */ + public void startDocument() throws SAXException { + } + + /** + * @see org.xml.sax.ContentHandler#endDocument() + */ + public void endDocument() throws SAXException { + } + + /** + * @see org.xml.sax.ContentHandler#characters(char[], int, int) + */ + public void characters(char[] ch, int start, int length) throws SAXException { + try { + Object o = stack.peek(); + if(o instanceof Depends) { + String s = new String(ch, start, length); + Depends d = (Depends)o; + if (d.getName() != null) + d.setName(d.getName() + s.trim()); + else + d.setName(s.trim()); + } + } catch(RuntimeException e) { + log.log("exception3333333333333333333", Project.MSG_INFO); + throw new SAXParseException("exception", locator, e); + } + } + + /** + * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int) + */ + public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { + } + + /** + * @see org.xml.sax.ContentHandler#processingInstruction(java.lang.String, java.lang.String) + */ + public void processingInstruction(String target, String data) throws SAXException { + } +}
\ No newline at end of file diff --git a/src/main/java/net/sf/antcontrib/design/InstructionVisitor.java b/src/main/java/net/sf/antcontrib/design/InstructionVisitor.java new file mode 100644 index 0000000..eb4c920 --- /dev/null +++ b/src/main/java/net/sf/antcontrib/design/InstructionVisitor.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2001-2004 Ant-Contrib project. All rights reserved. + * + * 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 net.sf.antcontrib.design; + +import org.apache.bcel.generic.ANEWARRAY; +import org.apache.bcel.generic.CHECKCAST; +import org.apache.bcel.generic.ConstantPoolGen; +import org.apache.bcel.generic.EmptyVisitor; +import org.apache.bcel.generic.INSTANCEOF; +import org.apache.bcel.generic.INVOKESTATIC; +import org.apache.bcel.generic.LoadInstruction; +import org.apache.bcel.generic.NEW; +import org.apache.bcel.generic.PUTSTATIC; +import org.apache.bcel.generic.Type; +import org.apache.tools.ant.Project; + + + +public class InstructionVisitor extends EmptyVisitor { + + + private ConstantPoolGen poolGen; + private Log log; + private Design design; + + /** + * @param poolGen + * @param v + */ + public InstructionVisitor(ConstantPoolGen poolGen, Log log, Design d) { + this.poolGen = poolGen; + this.log = log; + this.design = d; + } + + public void visitCHECKCAST(CHECKCAST c) { + Type t = c.getType(poolGen); + log.log(" instr(checkcast)="+t, Project.MSG_DEBUG); + String type = t.toString(); + + design.checkClass(type); + } + + public void visitLoadInstruction(LoadInstruction l) { + //log.log(" visit load", Project.MSG_DEBUG); + Type t = l.getType(poolGen); + log.log(" instr(loadinstr)="+t, Project.MSG_DEBUG); + String type = t.toString(); + + design.checkClass(type); + } + + public void visitNEW(NEW n) { + Type t= n.getType(poolGen); + log.log(" instr(new)="+t, Project.MSG_DEBUG); + String type = t.toString(); + + design.checkClass(type); + } + + public void visitANEWARRAY(ANEWARRAY n) { + Type t = n.getType(poolGen); + log.log(" instr(anewarray)="+t, Project.MSG_DEBUG); + String type = t.toString(); + + design.checkClass(type); + } + + public void visitINSTANCEOF(INSTANCEOF i) { + Type t = i.getType(poolGen); + log.log(" instr(instanceof)="+t, Project.MSG_DEBUG); + String type = t.toString(); + + design.checkClass(type); + } + public void visitINVOKESTATIC(INVOKESTATIC s) { + String t = s.getClassName(poolGen); + log.log(" instr(invokestatic)="+t, Project.MSG_DEBUG); + + design.checkClass(t); + } + + public void visitPUTSTATIC(PUTSTATIC s) { + String one = s.getClassName(poolGen); + String two = s.getFieldName(poolGen); + String three = s.getName(poolGen); + String four = s.getSignature(poolGen); + String five = s.getClassType(poolGen)+""; + String six = s.getFieldType(poolGen)+""; + log.log(" instr(putstatic)a="+one, Project.MSG_DEBUG); + log.log(" instr(putstatic)b="+two, Project.MSG_DEBUG); + log.log(" instr(putstatic)c="+three, Project.MSG_DEBUG); + log.log(" instr(putstatic)d="+four, Project.MSG_DEBUG); + log.log(" instr(putstatic)e="+five, Project.MSG_DEBUG); + log.log(" instr(putstatic)f="+six, Project.MSG_DEBUG); + + String className = s.getFieldName(poolGen); + if("staticField".equals(className)) + return; + + if(className.startsWith("class$") || className.startsWith("array$")) + ; + else return; + + log.log(" instr(putstatic)1="+className, Project.MSG_DEBUG); + className = className.substring(6, className.length()); + log.log(" instr(putstatic)2="+className, Project.MSG_DEBUG); + className = className.replace('$', '.'); + log.log(" instr(putstatic)3="+className, Project.MSG_DEBUG); + + design.checkClass(className); + } +}
\ No newline at end of file diff --git a/src/main/java/net/sf/antcontrib/design/Log.java b/src/main/java/net/sf/antcontrib/design/Log.java new file mode 100644 index 0000000..466dcdc --- /dev/null +++ b/src/main/java/net/sf/antcontrib/design/Log.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2001-2004 Ant-Contrib project. All rights reserved. + * + * 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. + */ +/* + * Created on Dec 24, 2004 + */ +package net.sf.antcontrib.design; + +/** + * + * @author dhiller + */ +public interface Log { + + public void log(String s, int level); +} diff --git a/src/main/java/net/sf/antcontrib/design/Package.java b/src/main/java/net/sf/antcontrib/design/Package.java new file mode 100644 index 0000000..a988975 --- /dev/null +++ b/src/main/java/net/sf/antcontrib/design/Package.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2001-2004 Ant-Contrib project. All rights reserved. + * + * 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 net.sf.antcontrib.design; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + + +/* + * Created on Aug 24, 2003 + * + * To change the template for this generated file go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +/** + * FILL IN JAVADOC HERE + * + * @author Dean Hiller([email protected]) + */ +public class Package { + + public final static String DEFAULT = "default package"; + private String name; + private String pack; + + //holds the name attribute of the package element of each + //package this package depends on. + private List depends; + private Set unusedDepends = new HashSet(); + private boolean isIncludeSubpackages; + private boolean needDeclarations; + private boolean needDepends; + private boolean isUsed = false; + + public void setName(String name) { + if("".equals(name)) + name = DEFAULT; + this.name = name; + } + public String getName() { + return name; + } + + public void setPackage(String pack) { + this.pack = pack; + } + + public String getPackage() { + return pack; + } + + public void addDepends(Depends d) { + if(depends == null) + depends = new ArrayList(); + depends.add(d); + unusedDepends.add(d); + } + + public Depends[] getDepends() { + Depends[] d = new Depends[0]; + if(depends == null) + return d; + return (Depends[])depends.toArray(d); + } + + /** + * @param b + */ + public void setIncludeSubpackages(boolean b) { + isIncludeSubpackages = b; + } + /** + * @return + */ + public boolean isIncludeSubpackages() { + return isIncludeSubpackages; + } + /** + * @param b + */ + public void setNeedDeclarations(boolean b) { + needDeclarations = b; + } + /** + * @return + */ + public boolean isNeedDeclarations() { + return needDeclarations; + } + /** + * @param b + */ + public void setNeedDepends(boolean b) { + needDepends = b; + } + + public boolean getNeedDepends() { + return needDepends; + } + /** + * @param b + */ + public void setUsed(boolean b) + { + isUsed = b; + } + public boolean isUsed() + { + return isUsed; + } + /** + * @param d + */ + public void addUsedDependency(Depends d) + { + unusedDepends.remove(d); + } + + public Set getUnusedDepends() { + return unusedDepends; + } + +} + diff --git a/src/main/java/net/sf/antcontrib/design/VerifyDesign.java b/src/main/java/net/sf/antcontrib/design/VerifyDesign.java new file mode 100644 index 0000000..dfffd47 --- /dev/null +++ b/src/main/java/net/sf/antcontrib/design/VerifyDesign.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2004-2005 Ant-Contrib project. All rights reserved. + * + * 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 net.sf.antcontrib.design; + +import java.io.File; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.types.Path; + +/** + * @author dhiller + */ +public class VerifyDesign + extends Task + implements Log { + + private VerifyDesignDelegate delegate; + + public VerifyDesign() { + delegate = new VerifyDesignDelegate(this); + } + + public void setJar(File f) { + delegate.setJar(f); + } + + public void setDesign(File f) { + delegate.setDesign(f); + } + + public void setCircularDesign(boolean isCircularDesign) { + delegate.setCircularDesign(isCircularDesign); + } + + public void setDeleteFiles(boolean deleteFiles) { + delegate.setDeleteFiles(deleteFiles); + } + + public void setFillInBuildException(boolean b) { + delegate.setFillInBuildException(b); + } + + public void setNeedDeclarationsDefault(boolean b) { + delegate.setNeedDeclarationsDefault(b); + } + + public void setNeedDependsDefault(boolean b) { + delegate.setNeedDependsDefault(b); + } + + public void addConfiguredPath(Path path) { + delegate.addConfiguredPath(path); + } + public void execute() + throws BuildException { + delegate.execute(); + } +} diff --git a/src/main/java/net/sf/antcontrib/design/VerifyDesignDelegate.java b/src/main/java/net/sf/antcontrib/design/VerifyDesignDelegate.java new file mode 100644 index 0000000..3471ec9 --- /dev/null +++ b/src/main/java/net/sf/antcontrib/design/VerifyDesignDelegate.java @@ -0,0 +1,429 @@ +/*
+ * Copyright (c) 2004-2005 Ant-Contrib project. All rights reserved.
+ *
+ * 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 net.sf.antcontrib.design;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Vector;
+import java.util.jar.JarFile;
+import java.util.zip.ZipEntry;
+
+import org.apache.bcel.Constants;
+import org.apache.bcel.classfile.ClassFormatException;
+import org.apache.bcel.classfile.ClassParser;
+import org.apache.bcel.classfile.Constant;
+import org.apache.bcel.classfile.ConstantClass;
+import org.apache.bcel.classfile.ConstantPool;
+import org.apache.bcel.classfile.ConstantUtf8;
+import org.apache.bcel.classfile.DescendingVisitor;
+import org.apache.bcel.classfile.JavaClass;
+import org.apache.bcel.classfile.Utility;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.PatternSet;
+import org.apache.tools.ant.util.JAXPUtils;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.XMLReader;
+
+/**
+ *
+ *
+ *
+ * @author dhiller
+ *
+ */
+
+public class VerifyDesignDelegate implements Log {
+
+ private File designFile;
+ private Vector paths = new Vector();
+ private boolean isCircularDesign = false;
+ private boolean deleteFiles = false;
+ private boolean fillInBuildException = false;
+ private boolean needDeclarationsDefault = true;
+ private boolean needDependsDefault = true;
+
+ private Task task;
+ private Design design;
+ private HashSet primitives = new HashSet();
+ private Vector designErrors = new Vector();
+ private boolean verifiedAtLeastOne = false;
+
+ public VerifyDesignDelegate(Task task) {
+ this.task = task;
+ primitives.add("B");
+ primitives.add("C");
+ primitives.add("D");
+ primitives.add("F");
+ primitives.add("I");
+ primitives.add("J");
+ primitives.add("S");
+ primitives.add("Z");
+ }
+
+ public void addConfiguredPath(Path path) {
+// Path newPath = new Path(task.getProject());
+// path.
+
+
+ paths.add(path);
+ }
+
+ public void setJar(File f) {
+ Path p = (Path)task.getProject().createDataType("path");
+ p.createPathElement().setLocation(f.getAbsoluteFile());
+ addConfiguredPath(p);
+ }
+
+ public void setDesign(File f) {
+ this.designFile = f;
+ }
+
+ public void setCircularDesign(boolean isCircularDesign) {
+ this.isCircularDesign = isCircularDesign;
+ }
+
+ public void setDeleteFiles(boolean deleteFiles) {
+ this.deleteFiles = deleteFiles;
+ }
+
+ public void setFillInBuildException(boolean b) {
+ fillInBuildException = b;
+ }
+
+ public void setNeedDeclarationsDefault(boolean b) {
+ needDeclarationsDefault = b;
+ }
+
+ public void setNeedDependsDefault(boolean b) {
+ needDependsDefault = b;
+ }
+
+ public void execute() {
+ if(!designFile.exists() || designFile.isDirectory())
+ throw new BuildException("design attribute in verifydesign element specified an invalid file="+designFile);
+
+ verifyJarFilesExist();
+
+ try {
+ XMLReader reader = JAXPUtils.getXMLReader();
+ DesignFileHandler ch = new DesignFileHandler(this, designFile, isCircularDesign, task.getLocation());
+ ch.setNeedDeclarationsDefault(needDeclarationsDefault);
+ ch.setNeedDependsDefault(needDependsDefault);
+ reader.setContentHandler(ch);
+ //reader.setEntityResolver(ch);
+ //reader.setErrorHandler(ch);
+ //reader.setDTDHandler(ch);
+
+ log("about to start parsing file='"+designFile+"'", Project.MSG_INFO);
+ FileInputStream fileInput = new FileInputStream(designFile);
+ InputSource src = new InputSource(fileInput);
+ reader.parse(src);
+ design = ch.getDesign();
+
+ Enumeration pathsEnum = paths.elements();
+ Path p = null;
+ while (pathsEnum.hasMoreElements()) {
+ p = (Path)pathsEnum.nextElement();
+ verifyPathAdheresToDesign(design, p);
+ }
+
+ //only put unused errors if there are no other errors
+ //this is because you end up with false unused errors if you don't do this.
+ if(designErrors.isEmpty())
+ design.fillInUnusedPackages(designErrors);
+
+ if (! designErrors.isEmpty()) {
+ log(designErrors.size()+"Errors.", Project.MSG_WARN);
+ if(!fillInBuildException)
+ throw new BuildException("Design check failed due to previous errors");
+ throwAllErrors();
+ }
+
+ } catch (SAXException e) {
+ maybeDeleteFiles();
+ if (e.getException() != null
+ && e.getException() instanceof RuntimeException)
+ throw (RuntimeException) e.getException();
+ else if (e instanceof SAXParseException) {
+ SAXParseException pe = (SAXParseException) e;
+ throw new BuildException("\nProblem parsing design file='"
+ + designFile + "'. \nline=" + pe.getLineNumber()
+ + " column=" + pe.getColumnNumber() + " Reason:\n"
+ + e.getMessage() + "\n", e);
+ }
+ throw new BuildException("\nProblem parsing design file='"
+ + designFile + "'. Reason:\n" + e, e);
+ } catch (IOException e) {
+ maybeDeleteFiles();
+ throw new RuntimeException("See attached exception", e);
+ // throw new BuildException("IOException on design file='"
+ // + designFile + "'. attached:", e);
+ } catch(RuntimeException e) {
+ maybeDeleteFiles();
+ throw e;
+ } finally {
+
+ }
+
+ if(!verifiedAtLeastOne)
+ throw new BuildException("Did not find any class or jar files to verify");
+ }
+ //some auto builds like cruisecontrol can only report all the
+ //standard ant task errors and the build exceptions so here
+ //we need to fill in the buildexception so the errors are reported
+ //correctly through those tools....though, you think ant has a hook
+ //in that cruisecontrol is not using like LogListeners or something
+ private void throwAllErrors() {
+ String result = "Design check failed due to following errors";
+ Enumeration exceptions = designErrors.elements();
+ while(exceptions.hasMoreElements()) {
+ BuildException be = (BuildException)exceptions.nextElement();
+ String message = be.getMessage();
+ result += "\n" + message;
+ }
+ throw new BuildException(result);
+ }
+
+ private void verifyJarFilesExist() {
+ Enumeration pathsEnum = paths.elements();
+ Path p = null;
+ while (pathsEnum.hasMoreElements()) {
+ p = (Path)pathsEnum.nextElement();
+ String files[] = p.list();
+ for (int i=0;i<files.length;i++) {
+ File file = new File(files[i]);
+
+ if (!file.exists())
+ throw new BuildException(VisitorImpl.getNoFileMsg(file));
+ }
+ }
+ }
+
+ private void maybeDeleteFiles() {
+ if (deleteFiles) {
+ log("Deleting all class and jar files so you do not get tempted to\n" +
+ "use a jar that doesn't abide by the design(This option can\n" +
+ "be turned off if you really want)", Project.MSG_INFO);
+
+ Enumeration pathsEnum = paths.elements();
+ Path p = null;
+ while (pathsEnum.hasMoreElements()) {
+ p = (Path)pathsEnum.nextElement();
+ deleteFilesInPath(p);
+ }
+ }
+ }
+
+ private void deleteFilesInPath(Path p) {
+ String files[] = p.list();
+ for (int i=0;i<files.length;i++) {
+ File file = new File(files[i]);
+
+ boolean deleted = file.delete();
+ if (! deleted) {
+ file.deleteOnExit();
+ }
+ }
+ }
+
+ private void verifyPathAdheresToDesign(Design d, Path p) throws ClassFormatException, IOException {
+ String files[] = p.list();
+ for (int i=0;i<files.length;i++) {
+ File file = new File(files[i]);
+ if(file.isDirectory()) {
+ FileSet set = new FileSet();
+ set.setDir(file);
+ set.setProject(task.getProject());
+ PatternSet.NameEntry entry1 = set.createInclude();
+ PatternSet.NameEntry entry2 = set.createInclude();
+ PatternSet.NameEntry entry3 = set.createInclude();
+ entry1.setName("**/*.class");
+ entry2.setName("**/*.jar");
+ entry3.setName("**/*.war");
+ DirectoryScanner scanner = set.getDirectoryScanner(task.getProject());
+ scanner.setBasedir(file);
+ String[] scannerFiles = scanner.getIncludedFiles();
+ for(int j = 0; j < scannerFiles.length; j++) {
+ verifyPartOfPath(scannerFiles[j], new File(file, scannerFiles[j]), d);
+ }
+ } else
+ verifyPartOfPath(files[i], file, d);
+ }
+ }
+
+ private void verifyPartOfPath(String fileName, File file, Design d) throws IOException {
+ if (fileName.endsWith(".jar") || fileName.endsWith(".war")) {
+ JarFile jarFile = new JarFile(file);
+ verifyJarAdheresToDesign(d, jarFile, file);
+ } else if (fileName.endsWith(".class")) {
+ verifyClassAdheresToDesign(d, file);
+ } else
+ throw new BuildException("Only directories, jars, wars, and class files can be supplied to verify design, not file="+file.getAbsolutePath());
+ }
+
+ private void verifyClassAdheresToDesign(Design d, File classFile)
+ throws ClassFormatException, IOException {
+ FileInputStream fis = null;
+ try {
+ fis = new FileInputStream(classFile);
+ verifyClassAdheresToDesign(d, fis, classFile.getAbsolutePath(), classFile);
+ }
+ finally {
+ try {
+ if (fis != null) {
+ fis.close();
+ }
+ }
+ catch (IOException e) {
+ ; //doh!!
+ }
+ }
+
+ }
+
+ private void verifyJarAdheresToDesign(Design d, JarFile jarFile, File original)
+ throws ClassFormatException, IOException {
+
+ try {
+ Enumeration en = jarFile.entries();
+ while(en.hasMoreElements()) {
+ ZipEntry entry = (ZipEntry)en.nextElement();
+ InputStream in = null;
+ if(entry.getName().endsWith(".class")) {
+ in = jarFile.getInputStream(entry);
+ try {
+ in = jarFile.getInputStream(entry);
+ verifyClassAdheresToDesign(d, in, entry.getName(), original);
+ }
+ finally {
+ try {
+ if (in != null) {
+ in.close();
+ }
+ }
+ catch (IOException e) {
+ ; // doh!!!
+ }
+ }
+ }
+ }
+ }
+ finally {
+ try {
+ jarFile.close();
+ }
+ catch (IOException e) {
+ ; //doh!!!
+ }
+ }
+ }
+
+ private String className = "";
+
+ private void verifyClassAdheresToDesign(Design d, InputStream in, String name, File originalClassOrJarFile) throws ClassFormatException, IOException {
+ try {
+ verifiedAtLeastOne = true;
+ ClassParser parser = new ClassParser(in, name);
+ JavaClass javaClass = parser.parse();
+ className = javaClass.getClassName();
+
+ if(!d.needEvalCurrentClass(className))
+ return;
+
+ ConstantPool pool = javaClass.getConstantPool();
+ processConstantPool(pool);
+ VisitorImpl visitor = new VisitorImpl(pool, this, d, task.getLocation());
+ DescendingVisitor desc = new DescendingVisitor(javaClass, visitor);
+ desc.visit();
+ } catch(BuildException e) {
+ log(Design.getWrapperMsg(originalClassOrJarFile, e.getMessage()), Project.MSG_ERR);
+ designErrors.addElement(e);
+ }
+ }
+
+ private void processConstantPool(ConstantPool pool) {
+ Constant[] constants = pool.getConstantPool();
+ if(constants == null) {
+ log(" constants=null", Project.MSG_VERBOSE);
+ return;
+ }
+
+ log(" constants len="+constants.length, Project.MSG_VERBOSE);
+ for(int i = 0; i < constants.length; i++) {
+ processConstant(pool, constants[i], i);
+ }
+ }
+
+ private void processConstant(ConstantPool pool, Constant c, int i) {
+ if(c == null) //don't know why, but constant[0] seems to be always null.
+ return;
+
+ log(" const["+i+"]="+pool.constantToString(c)+" inst="+c.getClass().getName(), Project.MSG_DEBUG);
+ byte tag = c.getTag();
+ switch(tag) {
+ //reverse engineered from ConstantPool.constantToString..
+ case Constants.CONSTANT_Class:
+ int ind = ((ConstantClass)c).getNameIndex();
+ c = pool.getConstant(ind, Constants.CONSTANT_Utf8);
+ String className = Utility.compactClassName(((ConstantUtf8)c).getBytes(), false);
+ log(" classNamePre="+className, Project.MSG_DEBUG);
+ className = getRidOfArray(className);
+ String firstLetter = className.charAt(0)+"";
+ if(primitives.contains(firstLetter))
+ return;
+ log(" className="+className, Project.MSG_VERBOSE);
+ design.checkClass(className);
+ break;
+ default:
+
+ }
+ }
+
+ private static String getRidOfArray(String className) {
+ while(className.startsWith("["))
+ className = className.substring(1, className.length());
+ return className;
+ }
+
+ public static String getPackageName(String className) {
+ String packageName = Package.DEFAULT;
+ int index = className.lastIndexOf(".");
+ if(index > 0)
+ packageName = className.substring(0, index);
+ //DEANDO: test the else scenario here(it is a corner case)...
+
+ return packageName;
+ }
+
+ public void log(String msg, int level) {
+ //if(level == Project.MSG_WARN || level == Project.MSG_INFO
+ // || level == Project.MSG_ERR || level == Project.MSG_VERBOSE)
+ //VerifyDesignTest.log(msg);
+ task.log(msg, level);
+ }
+}
diff --git a/src/main/java/net/sf/antcontrib/design/VisitorImpl.java b/src/main/java/net/sf/antcontrib/design/VisitorImpl.java new file mode 100644 index 0000000..84aefee --- /dev/null +++ b/src/main/java/net/sf/antcontrib/design/VisitorImpl.java @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2001-2004 Ant-Contrib project. All rights reserved. + * + * 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. + */ +/* + * Created on Jan 9, 2005 + */ +package net.sf.antcontrib.design; + +import java.io.File; + +import org.apache.bcel.Constants; +import org.apache.bcel.classfile.Code; +import org.apache.bcel.classfile.CodeException; +import org.apache.bcel.classfile.ConstantPool; +import org.apache.bcel.classfile.EmptyVisitor; +import org.apache.bcel.classfile.ExceptionTable; +import org.apache.bcel.classfile.Field; +import org.apache.bcel.classfile.JavaClass; +import org.apache.bcel.classfile.LineNumberTable; +import org.apache.bcel.classfile.LocalVariable; +import org.apache.bcel.classfile.Method; +import org.apache.bcel.classfile.Utility; +import org.apache.bcel.generic.ConstantPoolGen; +import org.apache.bcel.generic.Instruction; +import org.apache.bcel.generic.InstructionHandle; +import org.apache.bcel.generic.MethodGen; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Location; +import org.apache.tools.ant.Project; + +class VisitorImpl extends EmptyVisitor { + + private ConstantPool pool; + private Log log; + private Design design; + private ConstantPoolGen poolGen; + private InstructionVisitor visitor; + private Location location; + + public VisitorImpl(ConstantPool pool, Log log, Design d, Location loc) { + this.pool = pool; + this.log = log; + this.design = d; + this.location = loc; + this.poolGen = new ConstantPoolGen(pool); + visitor = new InstructionVisitor(poolGen, log, d); + } + + private void log(String s, int level) { + log.log(s, level); + } + + public void visitJavaClass(JavaClass c) { + log(" super=" + c.getSuperclassName(), Project.MSG_VERBOSE); + String[] names = c.getInterfaceNames(); + + String superClass = c.getSuperclassName(); + + design.checkClass(superClass); + + for (int i = 0; i < names.length; i++) { + log(" interfaces=" + names[i], Project.MSG_VERBOSE); + design.checkClass(names[i]); + } + } + + /** + * @see org.apache.bcel.classfile.Visitor#visitField(org.apache.bcel.classfile.Field) + */ + public void visitField(Field f) { + String type = Utility.methodSignatureReturnType(f.getSignature()); + log(" field type=" + type, Project.MSG_VERBOSE); + design.checkClass(type); + + } + + /** + * @see org.apache.bcel.classfile.Visitor#visitLocalVariable(org.apache.bcel.classfile.LocalVariable) + */ + public void visitLocalVariable(LocalVariable v) { + String type = Utility.methodSignatureReturnType(v.getSignature()); + log(" localVar type=" + type, Project.MSG_VERBOSE); + design.checkClass(type); + } + + /** + * @see org.apache.bcel.classfile.Visitor#visitMethod(org.apache.bcel.classfile.Method) + */ + public void visitMethod(Method m) { + log(" method=" + m.getName(), Project.MSG_VERBOSE); + String retType = Utility.methodSignatureReturnType(m.getSignature()); + log(" method ret type=" + retType, Project.MSG_VERBOSE); + if (!"void".equals(retType)) + design.checkClass(retType); + + String[] types = Utility.methodSignatureArgumentTypes(m.getSignature()); + for (int i = 0; i < types.length; i++) { + log(" method param[" + i + "]=" + types[i], + Project.MSG_VERBOSE); + design.checkClass(types[i]); + } + + ExceptionTable excs = m.getExceptionTable(); + if (excs != null) { + types = excs.getExceptionNames(); + for (int i = 0; i < types.length; i++) { + log(" exc=" + types[i], Project.MSG_VERBOSE); + design.checkClass(types[i]); + } + } + + processInstructions(m); + } + + private void processInstructions(Method m) { + MethodGen mg = new MethodGen(m, design.getCurrentClass(), poolGen); + + if (!mg.isAbstract() && !mg.isNative()) { + InstructionHandle ih = mg.getInstructionList().getStart(); + for (; ih != null; ih = ih.getNext()) { + Instruction i = ih.getInstruction(); + log(" instr=" + i, Project.MSG_DEBUG); + // if (i instanceof BranchInstruction) { + // branch_map.put(i, ih); // memorize container + // } + + // if (ih.hasTargeters()) { + // if (i instanceof BranchInstruction) { + // _out.println(" InstructionHandle ih_" + // + ih.getPosition() + ";"); + // } else { + // _out.print(" InstructionHandle ih_" + // + ih.getPosition() + " = "); + // } + // } else { + // _out.print(" "); + // } + + // if (!visitInstruction(i)) + i.accept(visitor); + } + + // CodeExceptionGen[] handlers = mg.getExceptionHandlers(); + // + // log("handlers len="+handlers.length, Project.MSG_DEBUG); + // for (int i = 0; i < handlers.length; i++) { + // CodeExceptionGen h = handlers[i]; + // ObjectType t = h.getCatchType(); + // log("type="+t, Project.MSG_DEBUG); + // if(t != null) { + // log("type="+t.getClassName(), Project.MSG_DEBUG); + // } + // } + // updateExceptionHandlers(); + } + } + + public void visitCodeException(CodeException c) { + String s = c.toString(pool, false); + + int catch_type = c.getCatchType(); + + if (catch_type == 0) + return; + + String temp = pool.getConstantString(catch_type, + Constants.CONSTANT_Class); + String str = Utility.compactClassName(temp, false); + + log(" catch=" + str, Project.MSG_DEBUG); + design.checkClass(str); + } + + // + public void visitCode(Code c) { + LineNumberTable table = c.getLineNumberTable(); + // LocalVariableTable table = c.getLocalVariableTable(); + if (table == null) + throw new BuildException(getNoDebugMsg(design.getCurrentClass()), location); + } + + public static String getNoDebugMsg(String className) { + String s = "Class="+className+" was not compiled with the debug option(-g) and\n" + + "therefore verifydesign cannot be used on this jar. Please compile your code\n"+ + "with -g option in javac or debug=\"true\" in the ant build.xml file"; + return s; + } + + /** + * @param jarName + * @return + */ + public static String getNoFileMsg(File jarName) { + String s = "File you specified in your path(or jar attribute)='"+jarName.getAbsolutePath()+"' does not exist"; + return s; + } + + +}
\ No newline at end of file |