KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > tools > license > ValidateLicenseHeaders


1 /*
2 * JBoss, Home of Professional Open Source
3 * Copyright 2005, JBoss Inc., and individual contributors as indicated
4 * by the @authors tag. See the copyright.txt in the distribution for a
5 * full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */

22 package org.jboss.tools.license;
23
24 import java.io.File JavaDoc;
25 import java.io.FileFilter JavaDoc;
26 import java.io.IOException JavaDoc;
27 import java.io.FileWriter JavaDoc;
28 import java.io.FileInputStream JavaDoc;
29 import java.io.FileOutputStream JavaDoc;
30 import java.io.RandomAccessFile JavaDoc;
31 import java.io.SyncFailedException JavaDoc;
32 import java.util.ArrayList JavaDoc;
33 import java.util.List JavaDoc;
34 import java.util.Iterator JavaDoc;
35 import java.util.Map JavaDoc;
36 import java.util.TreeMap JavaDoc;
37 import java.util.logging.Level JavaDoc;
38 import java.util.logging.Logger JavaDoc;
39 import java.nio.channels.FileChannel JavaDoc;
40 import java.nio.ByteBuffer JavaDoc;
41 import java.net.URL JavaDoc;
42 import javax.xml.parsers.DocumentBuilder JavaDoc;
43 import javax.xml.parsers.DocumentBuilderFactory JavaDoc;
44
45 import org.w3c.dom.Document JavaDoc;
46 import org.w3c.dom.Element JavaDoc;
47 import org.w3c.dom.NodeList JavaDoc;
48 import org.w3c.dom.Node JavaDoc;
49
50 /**
51  * A utility which scans all java source files in the cvs tree and validates
52  * the license header prior to the package statement for headers that match
53  * those declared in thirdparty/licenses/license-info.xml
54  *
55  * @author Scott.Stark@jboss.org
56  * @version $Revision: 38646 $
57  */

58 public class ValidateLicenseHeaders
59 {
60    /** Used to strip out diffs due to copyright date ranges */
61    static final String JavaDoc COPYRIGHT_REGEX = "copyright\\s(\\(c\\))*\\s*[\\d]+(\\s*,\\s*[\\d]+|\\s*-\\s*[\\d]+)*";
62
63    static final String JavaDoc DEFAULT_HEADER = "/*\n" +
64   " * JBoss, Home of Professional Open Source\n" +
65   " * Copyright 2005, JBoss Inc., and individual contributors as indicated\n" +
66   " * by the @authors tag. See the copyright.txt in the distribution for a\n" +
67   " * full listing of individual contributors.\n" +
68   " *\n" +
69   " * This is free software; you can redistribute it and/or modify it\n" +
70   " * under the terms of the GNU Lesser General Public License as\n" +
71   " * published by the Free Software Foundation; either version 2.1 of\n" +
72   " * the License, or (at your option) any later version.\n" +
73   " *\n" +
74   " * This software is distributed in the hope that it will be useful,\n" +
75   " * but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +
76   " * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n" +
77   " * Lesser General Public License for more details.\n" +
78   " *\n" +
79   " * You should have received a copy of the GNU Lesser General Public\n" +
80   " * License along with this software; if not, write to the Free\n" +
81   " * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n" +
82   " * 02110-1301 USA, or see the FSF site: http://www.fsf.org.\n" +
83   " */\n";
84    static Logger JavaDoc log = Logger.getLogger("ValidateCopyrightHeaders");
85    static boolean addDefaultHeader = true;
86    static FileFilter JavaDoc dotJavaFilter = new DotJavaFilter();
87    /**
88     * The term-headers from the thirdparty/license/license-info.xml
89     */

90    static TreeMap JavaDoc licenseHeaders = new TreeMap JavaDoc();
91    /**
92     * Java source files with no license header
93     */

94    static ArrayList JavaDoc noheaders = new ArrayList JavaDoc();
95    /**
96     * Java source files with a header that does not match one from licenseHeaders
97     */

98    static ArrayList JavaDoc invalidheaders = new ArrayList JavaDoc();
99    /** Total java source files seen */
100    static int totalCount;
101    /** Total out of date jboss headers seen */
102    static int jbossCount;
103
104    /**
105     * ValidateLicenseHeaders jboss-src-root
106     * @param args
107     */

108    public static void main(String JavaDoc[] args)
109       throws Exception JavaDoc
110    {
111       if( args.length == 0 || args[0].startsWith("-h") )
112       {
113          log.info("Usage: ValidateLicenseHeaders [-addheader] jboss-src-root");
114          System.exit(1);
115       }
116       int rootArg = 0;
117       if( args.length == 2 )
118       {
119          if( args[0].startsWith("-add") )
120             addDefaultHeader = true;
121          else
122          {
123             log.severe("Uknown argument: "+args[0]);
124             log.info("Usage: ValidateLicenseHeaders [-addheader] jboss-src-root");
125             System.exit(1);
126             
127          }
128          rootArg = 1;
129       }
130
131       File JavaDoc jbossSrcRoot = new File JavaDoc(args[rootArg]);
132       if( jbossSrcRoot.exists() == false )
133       {
134          log.info("Src root does not exist, check "+jbossSrcRoot.getAbsolutePath());
135          System.exit(1);
136       }
137
138       URL JavaDoc u = Thread.currentThread().getContextClassLoader().getResource("META-INF/services/javax.xml.parsers.DocumentBuilderFactory");
139       System.err.println(u);
140
141       // Load the valid copyright statements for the licenses
142
File JavaDoc licenseInfo = new File JavaDoc(jbossSrcRoot, "thirdparty/licenses/license-info.xml");
143       if( licenseInfo.exists() == false )
144       {
145          log.severe("Failed to find the thirdparty/licenses/license-info.xml under the src root");
146          System.exit(1);
147       }
148       DocumentBuilderFactory JavaDoc factory = DocumentBuilderFactory.newInstance();
149       DocumentBuilder JavaDoc db = factory.newDocumentBuilder();
150       Document JavaDoc doc = db.parse(licenseInfo);
151       NodeList JavaDoc licenses = doc.getElementsByTagName("license");
152       for(int i = 0; i < licenses.getLength(); i ++)
153       {
154          Element JavaDoc license = (Element JavaDoc) licenses.item(i);
155          String JavaDoc key = license.getAttribute("id");
156          ArrayList JavaDoc headers = new ArrayList JavaDoc();
157          licenseHeaders.put(key, headers);
158          NodeList JavaDoc copyrights = license.getElementsByTagName("terms-header");
159          for(int j = 0; j < copyrights.getLength(); j ++)
160          {
161             Element JavaDoc copyright = (Element JavaDoc) copyrights.item(j);
162             copyright.normalize();
163             String JavaDoc id = copyright.getAttribute("id");
164             // The id will be blank if there is no id attribute
165
if( id.length() == 0 )
166                continue;
167             String JavaDoc text = getElementContent(copyright);
168             if( text == null )
169                continue;
170             // Replace all duplicate whitespace and '*' with a single space
171
text = text.replaceAll("[\\s*]+", " ");
172             if( text.length() == 1)
173                continue;
174
175             text = text.toLowerCase().trim();
176             // Replace any copyright date0-date1,date2 with copyright ...
177
text = text.replaceAll(COPYRIGHT_REGEX, "...");
178             LicenseHeader lh = new LicenseHeader(id, text);
179             headers.add(lh);
180          }
181       }
182       log.fine(licenseHeaders.toString());
183
184       File JavaDoc[] files = jbossSrcRoot.listFiles(dotJavaFilter);
185       log.info("Root files count: "+files.length);
186       processSourceFiles(files, 0);
187
188       log.info("Processed "+totalCount);
189       log.info("Updated jboss headers: "+jbossCount);
190       // Files with no headers details
191
log.info("Files with no headers: "+noheaders.size());
192       FileWriter JavaDoc fw = new FileWriter JavaDoc("NoHeaders.txt");
193       for(Iterator JavaDoc iter = noheaders.iterator(); iter.hasNext();)
194       {
195          File JavaDoc f = (File JavaDoc) iter.next();
196          fw.write(f.getAbsolutePath());
197          fw.write('\n');
198       }
199       fw.close();
200
201       // Files with unknown headers details
202
log.info("Files with invalid headers: "+invalidheaders.size());
203       fw = new FileWriter JavaDoc("InvalidHeaders.txt");
204       for(Iterator JavaDoc iter = invalidheaders.iterator(); iter.hasNext();)
205       {
206          File JavaDoc f = (File JavaDoc) iter.next();
207          fw.write(f.getAbsolutePath());
208          fw.write('\n');
209       }
210       fw.close();
211
212       // License usage summary
213
log.info("Creating HeadersSummary.txt");
214       fw = new FileWriter JavaDoc("HeadersSummary.txt");
215       for(Iterator JavaDoc iter = licenseHeaders.entrySet().iterator(); iter.hasNext();)
216       {
217          Map.Entry JavaDoc entry = (Map.Entry JavaDoc) iter.next();
218          String JavaDoc key = (String JavaDoc) entry.getKey();
219          fw.write("+++ License type="+key);
220          fw.write('\n');
221          List JavaDoc list = (List JavaDoc) entry.getValue();
222          Iterator JavaDoc jiter = list.iterator();
223          while( jiter.hasNext() )
224          {
225             LicenseHeader lh = (LicenseHeader) jiter.next();
226             fw.write('\t');
227             fw.write(lh.id);
228             fw.write(", count=");
229             fw.write(""+lh.count);
230             fw.write('\n');
231          }
232       }
233       fw.close();
234    }
235
236    /**
237     * Get all non-comment content from the element.
238     * @param element
239     * @return the concatenated text/cdata content
240     */

241    public static String JavaDoc getElementContent(Element JavaDoc element)
242    {
243       if (element == null)
244          return null;
245
246       NodeList JavaDoc children = element.getChildNodes();
247       StringBuffer JavaDoc result = new StringBuffer JavaDoc();
248       for (int i = 0; i < children.getLength(); i++)
249       {
250          Node JavaDoc child = children.item(i);
251          if (child.getNodeType() == Node.TEXT_NODE ||
252              child.getNodeType() == Node.CDATA_SECTION_NODE)
253          {
254             result.append(child.getNodeValue());
255          }
256          else if( child.getNodeType() == Node.COMMENT_NODE )
257          {
258             // Ignore comment nodes
259
}
260          else
261          {
262             result.append(child.getFirstChild());
263          }
264       }
265       return result.toString().trim();
266    }
267
268    /**
269     * Validate the headers of all java source files
270     *
271     * @param files
272     * @param level
273     * @throws IOException
274     */

275    static void processSourceFiles(File JavaDoc[] files, int level)
276       throws IOException JavaDoc
277    {
278       for(int i = 0; i < files.length; i ++)
279       {
280          File JavaDoc f = files[i];
281          if( level == 0 )
282             log.info("processing "+f);
283          if( f.isDirectory() )
284          {
285             File JavaDoc[] children = f.listFiles(dotJavaFilter);
286             processSourceFiles(children, level+1);
287          }
288          else
289          {
290             parseHeader(f);
291          }
292       }
293    }
294
295    /**
296     * Read the first comment upto the package ...; statement
297     * @param javaFile
298     */

299    static void parseHeader(File JavaDoc javaFile)
300       throws IOException JavaDoc
301    {
302       totalCount ++;
303       RandomAccessFile JavaDoc raf = new RandomAccessFile JavaDoc(javaFile, "rw");
304       String JavaDoc line = raf.readLine();
305       StringBuffer JavaDoc tmp = new StringBuffer JavaDoc();
306       long endOfHeader = 0;
307       boolean packageOrImport = false;
308       while( line != null )
309       {
310          long nextEOH = raf.getFilePointer();
311          line = line.trim();
312          // Ignore any single line comments
313
if( line.startsWith("//") )
314          {
315             line = raf.readLine();
316             continue;
317          }
318
319          // If this is a package/import/class/interface statement break
320
if( line.startsWith("package") || line.startsWith("import")
321             || line.indexOf("class") >= 0 || line.indexOf("interface") >= 0 )
322          {
323             packageOrImport = true;
324             break;
325          }
326
327          // Update the current end of header marker
328
endOfHeader = nextEOH;
329
330          if( line.startsWith("/**") )
331             tmp.append(line.substring(3));
332          else if( line.startsWith("/*") )
333             tmp.append(line.substring(2));
334          else if( line.startsWith("*") )
335             tmp.append(line.substring(1));
336          else
337             tmp.append(line);
338          tmp.append(' ');
339          line = raf.readLine();
340       }
341       raf.close();
342
343       if( tmp.length() == 0 || packageOrImport == false )
344       {
345          addDefaultHeader(javaFile);
346          return;
347       }
348
349       String JavaDoc text = tmp.toString();
350       // Replace all duplicate whitespace with a single space
351
text = text.replaceAll("[\\s*]+", " ");
352       text = text.toLowerCase().trim();
353       // Replace any copyright date0-date1,date2 with copyright ...
354
text = text.replaceAll(COPYRIGHT_REGEX, "...");
355       if( tmp.length() == 0 )
356       {
357          addDefaultHeader(javaFile);
358          return;
359       }
360       // Search for a matching header
361
boolean matches = false;
362       String JavaDoc matchID = null;
363       Iterator JavaDoc iter = licenseHeaders.entrySet().iterator();
364       escape:
365       while( iter.hasNext() )
366       {
367          Map.Entry JavaDoc entry = (Map.Entry JavaDoc) iter.next();
368          String JavaDoc key = (String JavaDoc) entry.getKey();
369          List JavaDoc list = (List JavaDoc) entry.getValue();
370          Iterator JavaDoc jiter = list.iterator();
371          while( jiter.hasNext() )
372          {
373             LicenseHeader lh = (LicenseHeader) jiter.next();
374             if( text.startsWith(lh.text) )
375             {
376                matches = true;
377                matchID = lh.id;
378                lh.count ++;
379                lh.usage.add(javaFile);
380                if( log.isLoggable(Level.FINE) )
381                   log.fine(javaFile+" matches copyright key="+key+", id="+lh.id);
382                break escape;
383             }
384          }
385       }
386       text = null;
387       tmp.setLength(0);
388       if( matches == false )
389          invalidheaders.add(javaFile);
390       else if( matchID.startsWith("jboss") && matchID.endsWith("#0") == false )
391       {
392          // This is a legacy jboss head that needs to be updated to the default
393
replaceHeader(javaFile, endOfHeader);
394          jbossCount ++;
395       }
396    }
397
398    /**
399     * Replace a legacy jboss header with the current default header
400     * @param javaFile - the java source file
401     * @param endOfHeader - the offset to the end of the legacy header
402     * @throws IOException - thrown on failure to replace the header
403     */

404    static void replaceHeader(File JavaDoc javaFile, long endOfHeader)
405       throws IOException JavaDoc
406    {
407       if( log.isLoggable(Level.FINE) )
408          log.fine("Replacing legacy jboss header in: "+javaFile);
409       RandomAccessFile JavaDoc raf = new RandomAccessFile JavaDoc(javaFile, "rw");
410       File JavaDoc bakFile = new File JavaDoc(javaFile.getAbsolutePath()+".bak");
411       FileOutputStream JavaDoc fos = new FileOutputStream JavaDoc(bakFile);
412       fos.write(DEFAULT_HEADER.getBytes());
413       FileChannel JavaDoc fc = raf.getChannel();
414       long count = raf.length() - endOfHeader;
415       fc.transferTo(endOfHeader, count, fos.getChannel());
416       fc.close();
417       fos.close();
418       raf.close();
419       if( javaFile.delete() == false )
420          log.severe("Failed to delete java file: "+javaFile);
421       if( bakFile.renameTo(javaFile) == false )
422          throw new SyncFailedException JavaDoc("Failed to replace: "+javaFile);
423    }
424
425    /**
426     * Add the default jboss lgpl header
427     */

428    static void addDefaultHeader(File JavaDoc javaFile)
429       throws IOException JavaDoc
430    {
431       if( addDefaultHeader )
432       {
433          FileInputStream JavaDoc fis = new FileInputStream JavaDoc(javaFile);
434          FileChannel JavaDoc fc = fis.getChannel();
435          int size = (int) fc.size();
436          ByteBuffer JavaDoc contents = ByteBuffer.allocate(size);
437          fc.read(contents);
438          fis.close();
439          
440          ByteBuffer JavaDoc hdr = ByteBuffer.wrap(DEFAULT_HEADER.getBytes());
441          FileOutputStream JavaDoc fos = new FileOutputStream JavaDoc(javaFile);
442          fos.write(hdr.array());
443          fos.write(contents.array());
444          fos.close();
445       }
446
447       noheaders.add(javaFile);
448    }
449
450    /**
451     * A class that encapsulates the license id and valid terms header
452     */

453    static class LicenseHeader
454    {
455       String JavaDoc id;
456       String JavaDoc text;
457       int count;
458       ArrayList JavaDoc usage = new ArrayList JavaDoc();
459       LicenseHeader(String JavaDoc id, String JavaDoc text)
460       {
461          this.id = id;
462          this.text = text;
463       }
464    }
465
466    /**
467     * A filter which accepts files ending in .java (but not _Stub.java), or
468     * directories other than gen-src and gen-parsers
469     */

470    static class DotJavaFilter implements FileFilter JavaDoc
471    {
472       public boolean accept(File JavaDoc pathname)
473       {
474          boolean accept = false;
475          String JavaDoc name = pathname.getName();
476          if( pathname.isDirectory() )
477          {
478             // Ignore the gen-src directories for generated output
479
accept = name.equals("gen-src") == false
480                && name.equals("gen-parsers") == false;
481          }
482          else
483          {
484             accept = name.endsWith("_Stub.java") == false && name.endsWith(".java");
485          }
486          
487          return accept;
488       }
489    }
490 }
Popular Tags