summaryrefslogtreecommitdiffstats
path: root/src/main/java/net/sf/antcontrib/design
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/net/sf/antcontrib/design')
-rw-r--r--src/main/java/net/sf/antcontrib/design/Depends.java51
-rw-r--r--src/main/java/net/sf/antcontrib/design/Design.java330
-rw-r--r--src/main/java/net/sf/antcontrib/design/DesignFileHandler.java336
-rw-r--r--src/main/java/net/sf/antcontrib/design/InstructionVisitor.java126
-rw-r--r--src/main/java/net/sf/antcontrib/design/Log.java28
-rw-r--r--src/main/java/net/sf/antcontrib/design/Package.java139
-rw-r--r--src/main/java/net/sf/antcontrib/design/VerifyDesign.java72
-rw-r--r--src/main/java/net/sf/antcontrib/design/VerifyDesignDelegate.java429
-rw-r--r--src/main/java/net/sf/antcontrib/design/VisitorImpl.java211
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&gt;Preferences&gt;Java&gt;Code Generation&gt;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