001: /*
002: * Copyright (C) The Spice Group. All rights reserved.
003: *
004: * This software is published under the terms of the Spice
005: * Software License version 1.1, a copy of which has been included
006: * with this distribution in the LICENSE.txt file.
007: */
008: package org.codehaus.dna.tools.verifier;
009:
010: import java.io.File;
011: import java.io.FileInputStream;
012: import java.util.ArrayList;
013: import java.util.Iterator;
014: import java.util.List;
015:
016: import org.apache.tools.ant.AntClassLoader;
017: import org.apache.tools.ant.BuildException;
018: import org.apache.tools.ant.DirectoryScanner;
019: import org.apache.tools.ant.Project;
020: import org.apache.tools.ant.Task;
021: import org.apache.tools.ant.types.FileSet;
022: import org.apache.tools.ant.types.Path;
023: import org.codehaus.metaclass.Attributes;
024: import org.codehaus.metaclass.io.MetaClassIOBinary;
025: import org.codehaus.metaclass.model.Attribute;
026: import org.codehaus.metaclass.model.ClassDescriptor;
027:
028: /**
029: * Task to validate a set of components.
030: *
031: * @author Peter Donald
032: * @version $Revision: 1.1 $ $Date: 2004/04/18 20:13:45 $
033: */
034: public class VerifyComponentsTask extends Task {
035: /**
036: * IO used to read descriptors.
037: */
038: private static final MetaClassIOBinary IO = new MetaClassIOBinary();
039:
040: /**
041: * Extension used to designate class files.
042: */
043: private static final String CLASS_EXT = ".class";
044:
045: /**
046: * List of filesets to process.
047: */
048: private final List m_filesets = new ArrayList();
049:
050: /**
051: * The Classpath to load components from.
052: */
053: private Path m_classpath;
054:
055: /**
056: * Add fileset to list of files to be processed.
057: *
058: * @param fileSet fileset to list of files to be processed.
059: */
060: public void addFileset(final FileSet fileSet) {
061: m_filesets.add(fileSet);
062: getClassPath().addFileset(fileSet);
063: }
064:
065: /**
066: * Add a classpath element.
067: *
068: * @return the new path
069: */
070: public Path createClasspath() {
071: return getClassPath().createPath();
072: }
073:
074: /**
075: * Execute VerifyComponents.
076: */
077: public void execute() {
078: validateParameters();
079:
080: final AntClassLoader classLoader = new AntClassLoader(
081: getProject(), m_classpath);
082: boolean valid = true;
083:
084: final List classes = getClassesWithMetaData();
085: log("Verifying " + classes.size() + " components.");
086: final Iterator iterator = classes.iterator();
087: while (iterator.hasNext()) {
088: final String name = (String) iterator.next();
089: log("Verifying " + name + ".", Project.MSG_DEBUG);
090: valid &= validateComponent(classLoader, name);
091: }
092:
093: if (!valid) {
094: final String message = "Not all components validated.";
095: throw new BuildException(message);
096: }
097: }
098:
099: /**
100: * Validate specified component type.
101: *
102: * @param classLoader the classloader to load class from
103: * @param classname the name of type
104: * @return true if component is valid
105: */
106: boolean validateComponent(final AntClassLoader classLoader,
107: final String classname) {
108: boolean valid = true;
109: try {
110: final Class type = classLoader.loadClass(classname);
111: final ComponentVerifier verifier = new ComponentVerifier();
112: final VerifyIssue[] issues = verifier.verifyType(type);
113: for (int i = 0; i < issues.length; i++) {
114: final VerifyIssue issue = issues[i];
115: final String tail = " (" + classname + "): "
116: + issue.getDescription();
117: if (issue.isError()) {
118: log("Error" + tail, Project.MSG_ERR);
119: valid = false;
120: } else if (issue.isWarning()) {
121: log("Warning" + tail, Project.MSG_WARN);
122: valid = false;
123: } else {
124: log("Notice" + tail, Project.MSG_INFO);
125: }
126: }
127: } catch (final Exception e) {
128: final String message = "Failed to validate " + classname
129: + " due to " + e;
130: log(message, Project.MSG_ERR);
131: valid = false;
132: }
133: return valid;
134: }
135:
136: /**
137: * Validate specified parameters.
138: */
139: void validateParameters() {
140: if (null == m_classpath) {
141: final String message = "User did not specify classpath";
142: throw new BuildException(message);
143: }
144: }
145:
146: /**
147: * Setup list of files compiler will compile.
148: */
149: private List getClassesWithMetaData() {
150: final List list = new ArrayList();
151: final int count = m_filesets.size();
152: for (int i = 0; i < count; i++) {
153: final FileSet fileSet = (FileSet) m_filesets.get(i);
154: scanFileSetForClassesWithMetaData(fileSet, list);
155: }
156: return list;
157: }
158:
159: /**
160: * Add all files contained in fileset to compilers file list.
161: *
162: * @param fileSet the fileset
163: */
164: private void scanFileSetForClassesWithMetaData(
165: final FileSet fileSet, final List list) {
166: final File dir = fileSet.getDir(getProject());
167: final DirectoryScanner directoryScanner = fileSet
168: .getDirectoryScanner(getProject());
169: directoryScanner.scan();
170: final String[] includedFiles = directoryScanner
171: .getIncludedFiles();
172: for (int j = 0; j < includedFiles.length; j++) {
173: final String name = includedFiles[j];
174: if (name.endsWith(CLASS_EXT)) {
175: checkClass(name, dir, list);
176: }
177: }
178: }
179:
180: /**
181: * Check if class represented by specified file
182: * is a DNA component and classname to list.
183: *
184: * @param name the name of .class file
185: * @param dir the base directory
186: * @param list the list of classnames
187: */
188: void checkClass(final String name, final File dir, final List list) {
189: final String basename = name.substring(0, name.length()
190: - CLASS_EXT.length());
191: final String metaName = basename + MetaClassIOBinary.EXTENSION;
192: final File file = new File(dir, metaName);
193: final ClassDescriptor descriptor = loadDescriptor(file);
194: if (null != descriptor && isDNAComponent(descriptor)) {
195: list.add(descriptor.getName());
196: }
197: }
198:
199: /**
200: * Load descriptor from file.
201: * If descriptor can not be loaded then return null
202: *
203: * @param file the file
204: * @return the descriptor or null
205: */
206: ClassDescriptor loadDescriptor(final File file) {
207: try {
208: final FileInputStream input = new FileInputStream(file);
209: return IO.deserializeClass(input);
210: } catch (final Exception e) {
211: return null;
212: }
213: }
214:
215: /**
216: * Return true if specified descriptor represents DNA component.
217: *
218: * @param descriptor the descriptor
219: * @return true if descriptor represents DNA component
220: */
221: boolean isDNAComponent(final ClassDescriptor descriptor) {
222: final Attribute[] attributes = descriptor.getAttributes();
223: final Attribute attribute = Attributes.getAttributeByName(
224: attributes, "dna.component");
225: return null != attribute;
226: }
227:
228: /**
229: * Utility method to get a ClassPath instance.
230: *
231: * @return a Path object
232: */
233: private Path getClassPath() {
234: if (m_classpath == null) {
235: m_classpath = new Path(getProject());
236: }
237: return m_classpath;
238: }
239: }
|