You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Checklics.java 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676
  1. /* *******************************************************************
  2. * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
  3. * All rights reserved.
  4. * This program and the accompanying materials are made available
  5. * under the terms of the Eclipse Public License v1.0
  6. * which accompanies this distribution and is available at
  7. * http://www.eclipse.org/legal/epl-v10.html
  8. *
  9. * Contributors:
  10. * Xerox/PARC initial implementation
  11. * ******************************************************************/
  12. package org.aspectj.internal.tools.ant.taskdefs;
  13. import java.io.BufferedReader;
  14. import java.io.File;
  15. import java.io.FileReader;
  16. import java.io.FileWriter;
  17. import java.io.IOException;
  18. import java.io.PrintStream;
  19. import java.io.PrintWriter;
  20. import java.util.ArrayList;
  21. import java.util.Collections;
  22. import java.util.Hashtable;
  23. import java.util.Iterator;
  24. import java.util.List;
  25. import java.util.Map;
  26. import org.apache.tools.ant.BuildException;
  27. import org.apache.tools.ant.Project;
  28. import org.apache.tools.ant.taskdefs.MatchingTask;
  29. import org.apache.tools.ant.types.Path;
  30. import org.apache.tools.ant.types.Reference;
  31. /**
  32. * Check that included .java files contain license and copyright strings for MPL 1.0 (default), Apache, or CPL. Use list="true" to
  33. * get a list of known license variants {license}-{copyrightHolder} todo reimplement with regexp and jdiff FileLine utilities
  34. */
  35. public class Checklics extends MatchingTask {
  36. /*
  37. * This does not enforce that copyrights are correct/current, only that they exist. E.g., the default behavior requires MPL but
  38. * permits either Xerox or PARC copyright holders and any valid year.
  39. */
  40. public static final String MPL_TAG = "mpl";
  41. public static final String APACHE_TAG = "apache";
  42. public static final String CPL_IBM_PARC_TAG = "cpl-ibm|parc";
  43. public static final String CPL_IBM_TAG = "cpl-ibm";
  44. public static final String MPL_XEROX_PARC_TAG = "mpl-parc|xerox";
  45. public static final String MPL_ONLY_TAG = "mpl-only";
  46. public static final String MPL_PARC_TAG = "mpl-parc";
  47. public static final String PARC_COPYRIGHT_TAG = "parc-copy";
  48. public static final String CPL_IBM_PARC_XEROX_TAG = "cpl-ibm|parc|xerox";
  49. public static final String CPL_IBM_PARC_XEROX_OTHERS_TAG = "cpl-ibm|parc|xerox|others";
  50. public static final String EPL_CPL_IBM_PARC_XEROX_OTHERS_TAG = "epl-cpl-ibm|parc|xerox|vmware|others";
  51. public static final String DEFAULT = EPL_CPL_IBM_PARC_XEROX_OTHERS_TAG;
  52. static final Map<String,License> LICENSES; // unmodifiable Map
  53. static {
  54. final String CONTRIBUTORS = "Contributors";
  55. final String XEROX = "Xerox";
  56. final String PARC = "Palo Alto Research Center";
  57. final String APACHE = "The Apache Software Foundation";
  58. final String IBM = "IBM";
  59. final String VMWARE = "VMware";
  60. final String IBM_LONG = "International Business Machines";
  61. final String LIC_APL = "Apache Software Foundation (http://www.apache.org/)";
  62. final String LIC_MPL = "http://aspectj.org/MPL/";
  63. final String LIC_CPL = "Eclipse Public License";
  64. final String LIC_ECPL = " Public License";
  65. License APL = new License(APACHE_TAG, LIC_APL, APACHE);
  66. License MPL = new License(MPL_TAG, LIC_MPL, XEROX);
  67. License MPL_XEROX_PARC = new License(DEFAULT, LIC_MPL, XEROX, PARC);
  68. License CPL_IBM_PARC = new License(CPL_IBM_PARC_TAG, LIC_CPL, new String[] { IBM_LONG, IBM, PARC });
  69. License CPL_IBM_PARC_XEROX = new License(CPL_IBM_PARC_XEROX_TAG, LIC_CPL, new String[] { IBM_LONG, IBM, PARC, XEROX });
  70. License CPL_IBM_PARC_XEROX_OTHERS = new License(CPL_IBM_PARC_XEROX_OTHERS_TAG, LIC_CPL, new String[] { IBM_LONG, IBM, PARC,
  71. XEROX, CONTRIBUTORS });
  72. License EPL_CPL_IBM_PARC_XEROX_OTHERS = new License(EPL_CPL_IBM_PARC_XEROX_OTHERS_TAG, LIC_ECPL, new String[] { IBM_LONG,
  73. IBM, PARC, XEROX, VMWARE, CONTRIBUTORS });
  74. License CPL_IBM = new License(CPL_IBM_TAG, LIC_CPL, IBM, IBM_LONG);
  75. License MPL_ONLY = new License(MPL_ONLY_TAG, LIC_MPL);
  76. License MPL_PARC = new License(MPL_PARC_TAG, LIC_MPL, PARC);
  77. License PARC_COPYRIGHT = new License(PARC_COPYRIGHT_TAG, null, PARC);
  78. LICENSES = new Hashtable<String,License>();
  79. LICENSES.put(APL.tag, APL);
  80. LICENSES.put(MPL.tag, MPL);
  81. LICENSES.put(MPL_PARC.tag, MPL_PARC);
  82. LICENSES.put(MPL_XEROX_PARC.tag, MPL_XEROX_PARC);
  83. LICENSES.put(CPL_IBM_PARC.tag, CPL_IBM_PARC);
  84. LICENSES.put(MPL_ONLY.tag, MPL_ONLY);
  85. LICENSES.put(CPL_IBM.tag, CPL_IBM);
  86. LICENSES.put(PARC_COPYRIGHT.tag, PARC_COPYRIGHT);
  87. LICENSES.put(CPL_IBM_PARC_XEROX.tag, CPL_IBM_PARC_XEROX);
  88. LICENSES.put(CPL_IBM_PARC_XEROX_OTHERS.tag, CPL_IBM_PARC_XEROX_OTHERS);
  89. LICENSES.put(EPL_CPL_IBM_PARC_XEROX_OTHERS.tag, EPL_CPL_IBM_PARC_XEROX_OTHERS);
  90. }
  91. /** @param args String[] { &lt; sourcepath &gt; {, &lt; licenseTag &gt; } } */
  92. public static void main(String[] args) {
  93. switch (args.length) {
  94. case 1:
  95. runDirect(args[0], null, false);
  96. break;
  97. case 2:
  98. runDirect(args[0], args[1], false);
  99. break;
  100. default:
  101. String options = "{replace-headers|get-years|list|{licenseTag}}";
  102. System.err.println("java {me} sourcepath " + options);
  103. break;
  104. }
  105. }
  106. /**
  107. * Run the license check directly
  108. *
  109. * @param sourcepaths String[] of paths to source directories
  110. * @param license the String tag for the license, if any
  111. * @param failonerror boolean flag to pass to Checklics
  112. * @throws IllegalArgumentException if sourcepaths is empty
  113. * @return total number of failed licenses
  114. */
  115. public static int runDirect(String sourcepath, String license, boolean failonerror) {
  116. if ((null == sourcepath) || (1 > sourcepath.length())) {
  117. throw new IllegalArgumentException("bad sourcepath: " + sourcepath);
  118. }
  119. Checklics me = new Checklics();
  120. Project p = new Project();
  121. p.setName("direct interface to Checklics");
  122. p.setBasedir(".");
  123. me.setProject(p);
  124. me.setFailOnError(failonerror);
  125. me.setSourcepath(new Path(p, sourcepath));
  126. if (null != license) {
  127. if ("replace-headers".equals(license)) {
  128. me.setReplaceheaders(true);
  129. } else if ("get-years".equals(license)) {
  130. me.setGetYears(true);
  131. } else if ("list".equals(license)) {
  132. me.setList(true);
  133. } else {
  134. me.setLicense(license);
  135. }
  136. }
  137. me.execute();
  138. return me.failed;
  139. }
  140. private Path sourcepath;
  141. private License license;
  142. private boolean list;
  143. private String streamTag;
  144. private boolean failOnError;
  145. private boolean getYears;
  146. private boolean replaceHeaders;
  147. private int failed;
  148. private int passed;
  149. private boolean printDirectories;
  150. /** @param list if true, don't run but list known license tags */
  151. public void setList(boolean list) {
  152. this.list = list;
  153. }
  154. public void setPrintDirectories(boolean print) {
  155. printDirectories = print;
  156. }
  157. /**
  158. * When failOnError is true, if any file failed, throw BuildException listing number of files that file failed to pass license
  159. * check
  160. *
  161. * @param fail if true, report errors by throwing BuildException
  162. */
  163. public void setFailOnError(boolean fail) {
  164. this.failOnError = fail;
  165. }
  166. /** @param tl mpl | apache | cpl */
  167. public void setLicense(String tl) {
  168. License input = LICENSES.get(tl);
  169. if (null == input) {
  170. throw new BuildException("no license known for " + tl);
  171. }
  172. license = input;
  173. }
  174. public void setSourcepath(Path path) {
  175. if (sourcepath == null) {
  176. sourcepath = path;
  177. } else {
  178. sourcepath.append(path);
  179. }
  180. }
  181. public Path createSourcepath() {
  182. return sourcepath == null ? (sourcepath = new Path(project)) : sourcepath.createPath();
  183. }
  184. public void setSourcepathRef(Reference id) {
  185. createSourcepath().setRefid(id);
  186. }
  187. /** @param out "out" or "err" */
  188. public void setOutputStream(String out) {
  189. this.streamTag = out;
  190. }
  191. public void setReplaceheaders(boolean replaceHeaders) {
  192. this.replaceHeaders = replaceHeaders;
  193. }
  194. public void setGetYears(boolean getYears) {
  195. this.getYears = getYears;
  196. }
  197. /** list known licenses or check source tree */
  198. @Override
  199. public void execute() throws BuildException {
  200. if (list) {
  201. list();
  202. } else if (replaceHeaders) {
  203. replaceHeaders();
  204. } else if (getYears) {
  205. getYears();
  206. } else {
  207. checkLicenses();
  208. }
  209. }
  210. private PrintStream getOut() {
  211. return ("err".equals(streamTag) ? System.err : System.out);
  212. }
  213. interface FileVisitor {
  214. void visit(File file);
  215. }
  216. /** visit all .java files in all directories... */
  217. private void visitAll(FileVisitor visitor) {
  218. // List filelist = new ArrayList();
  219. String[] dirs = sourcepath.list();
  220. for (int i = 0; i < dirs.length; i++) {
  221. File dir = project.resolveFile(dirs[i]);
  222. String[] files = getDirectoryScanner(dir).getIncludedFiles();
  223. for (int j = 0; j < files.length; j++) {
  224. File file = new File(dir, files[j]);
  225. String path = file.getPath();
  226. if (path.endsWith(".java")) {
  227. visitor.visit(file);
  228. }
  229. }
  230. }
  231. }
  232. private void replaceHeaders() {
  233. class YearVisitor implements FileVisitor {
  234. @Override
  235. public void visit(File file) {
  236. HeaderInfo info = Header.checkFile(file);
  237. if (!Header.replaceHeader(file, info)) {
  238. throw new BuildException("failed to replace header for " + file + " using " + info);
  239. }
  240. }
  241. }
  242. visitAll(new YearVisitor());
  243. }
  244. private void getYears() {
  245. final PrintStream out = getOut();
  246. class YearVisitor implements FileVisitor {
  247. @Override
  248. public void visit(File file) {
  249. HeaderInfo info = Header.checkFile(file);
  250. out.println(info.toString());
  251. }
  252. }
  253. visitAll(new YearVisitor());
  254. }
  255. private void checkLicenses() throws BuildException {
  256. if (null == license) {
  257. setLicense(DEFAULT);
  258. }
  259. final License license = this.license; // being paranoid...
  260. if (null == license) {
  261. throw new BuildException("no license");
  262. }
  263. final PrintStream out = getOut();
  264. class Visitor implements FileVisitor {
  265. int failed = 0;
  266. int passed = 0;
  267. @Override
  268. public void visit(File file) {
  269. if (license.checkFile(file)) {
  270. passed++;
  271. } else {
  272. failed++;
  273. String path = file.getPath();
  274. if (!license.foundLicense()) {
  275. out.println(license.tag + " LICENSE FAIL: " + path);
  276. }
  277. if (!license.foundCopyright()) {
  278. out.println(license.tag + " COPYRIGHT FAIL: " + path);
  279. }
  280. }
  281. }
  282. }
  283. Visitor visitor = new Visitor();
  284. visitAll(visitor);
  285. this.failed = visitor.failed;
  286. this.passed = visitor.passed;
  287. if (0 < visitor.failed) {
  288. getOut().println("Total passed: " + visitor.passed + (visitor.failed == 0 ? "" : " failed: " + visitor.failed));
  289. if (failOnError) {
  290. throw new BuildException(failed + " files failed license check");
  291. }
  292. }
  293. }
  294. private void list() {
  295. Iterator enu = LICENSES.keySet().iterator();
  296. StringBuffer sb = new StringBuffer();
  297. sb.append("known license keys:");
  298. boolean first = true;
  299. while (enu.hasNext()) {
  300. sb.append((first ? " " : ", ") + enu.next());
  301. if (first) {
  302. first = false;
  303. }
  304. }
  305. getOut().println(sb.toString());
  306. }
  307. /**
  308. * Encapsulate license and copyright specifications to check files use hokey string matching.
  309. */
  310. public static class License {
  311. /** acceptable years for copyright prefix to company - append " " */
  312. static final String[] YEARS = // remove older after license xfer?
  313. new String[] { "2002 ", "2003 ", "2004 ", "2005", "2006", "2007", "2008",
  314. "2009", "2010", "2011", "2012", "2013", "2014", "2015", "2016", "2017", "2018", "2019", "2001 ", "2000 ",
  315. "1999 " };
  316. public final String tag;
  317. public final String license;
  318. private final String[] copyright;
  319. private boolean gotLicense;
  320. private boolean gotCopyright;
  321. License(String tag, String license) {
  322. this(tag, license, (String[]) null);
  323. }
  324. License(String tag, String license, String copyright) {
  325. this(tag, license, new String[] { copyright });
  326. }
  327. License(String tag, String license, String copyright, String altCopyright) {
  328. this(tag, license, new String[] { copyright, altCopyright });
  329. }
  330. License(String tag, String license, String[] copyright) {
  331. this.tag = tag;
  332. if ((null == tag) || (0 == tag.length())) {
  333. throw new IllegalArgumentException("null tag");
  334. }
  335. this.license = license;
  336. this.copyright = copyright;
  337. }
  338. public final boolean gotValidFile() {
  339. return foundLicense() && foundCopyright();
  340. }
  341. /** @return true if no license sought or if some license found */
  342. public final boolean foundLicense() {
  343. return ((null == license) || gotLicense);
  344. }
  345. /** @return true if no copyright sought or if some copyright found */
  346. public final boolean foundCopyright() {
  347. return ((null == copyright) || gotCopyright);
  348. }
  349. public boolean checkFile(final File file) {
  350. clear();
  351. // boolean result = false;
  352. BufferedReader input = null;
  353. int lineNum = 0;
  354. try {
  355. input = new BufferedReader(new FileReader(file));
  356. String line;
  357. while (!gotValidFile() && (line = input.readLine()) != null) {
  358. lineNum++;
  359. checkLine(line);
  360. }
  361. } catch (IOException e) {
  362. System.err.println("reading line " + lineNum + " of " + file);
  363. e.printStackTrace(System.err);
  364. } finally {
  365. if (null != input) {
  366. try {
  367. input.close();
  368. } catch (IOException e) {
  369. } // ignore
  370. }
  371. }
  372. return gotValidFile();
  373. }
  374. @Override
  375. public String toString() {
  376. return tag;
  377. }
  378. private void checkLine(String line) {
  379. if ((null == line) || (0 == line.length())) {
  380. return;
  381. }
  382. if (!gotLicense && (null != license) && (-1 != line.indexOf(license))) {
  383. gotLicense = true;
  384. }
  385. if (!gotCopyright && (null != copyright)) {
  386. int loc;
  387. for (int j = 0; !gotCopyright && (j < YEARS.length); j++) {
  388. if (-1 != (loc = line.indexOf(YEARS[j]))) {
  389. loc += YEARS[j].length();
  390. String afterLoc = line.substring(loc).trim();
  391. for (int i = 0; !gotCopyright && (i < copyright.length); i++) {
  392. if (0 == afterLoc.indexOf(copyright[i])) {
  393. gotCopyright = true;
  394. }
  395. }
  396. }
  397. }
  398. }
  399. }
  400. private void clear() {
  401. if (gotLicense) {
  402. gotLicense = false;
  403. }
  404. if (gotCopyright) {
  405. gotCopyright = false;
  406. }
  407. }
  408. } // class License
  409. }
  410. class HeaderInfo {
  411. /** File for which this is the info */
  412. public final File file;
  413. /** unmodifiable List of String years */
  414. public final List years;
  415. /** last line of license */
  416. public final int lastLine;
  417. /** last line of license */
  418. public final boolean hasLicense;
  419. public HeaderInfo(File file, int lastLine, List<String> years, boolean hasLicense) {
  420. this.lastLine = lastLine;
  421. this.file = file;
  422. this.hasLicense = hasLicense;
  423. List<String> newYears = new ArrayList<String>();
  424. newYears.addAll(years);
  425. Collections.sort(newYears);
  426. this.years = Collections.unmodifiableList(newYears);
  427. if ((null == file) || !file.canWrite()) {
  428. throw new IllegalArgumentException("bad file: " + this);
  429. }
  430. if (!hasLicense) {
  431. if ((0 > lastLine) || (65 < lastLine)) {
  432. throw new IllegalArgumentException("bad last line: " + this);
  433. }
  434. } else {
  435. if ((null == years) || (1 > years.size())) {
  436. throw new IllegalArgumentException("no years: " + this);
  437. }
  438. if ((20 > lastLine) || (65 < lastLine)) {
  439. throw new IllegalArgumentException("bad last line: " + this);
  440. }
  441. }
  442. }
  443. @Override
  444. public String toString() {
  445. return file.getPath() + ":" + lastLine + " " + years;
  446. }
  447. public void writeHeader(PrintWriter writer) {
  448. if (!hasLicense) {
  449. writer.println(TOP);
  450. writer.println(PARC_ONLY);
  451. writeRest(writer);
  452. } else {
  453. final int size = years.size();
  454. if (1 > size) {
  455. throw new Error("no years found in " + toString());
  456. }
  457. String first = (String) years.get(0);
  458. String last = (String) years.get(size - 1);
  459. boolean lastIs2002 = "2002".equals(last);
  460. String xlast = last;
  461. if (lastIs2002) { // 2002 was PARC
  462. xlast = (String) (size > 1 ? years.get(size - 2) : null);
  463. // 1999-2002 Xerox implies 1999-2001 Xerox
  464. if (first.equals(xlast) && !"2001".equals(xlast)) {
  465. xlast = "2001";
  466. }
  467. }
  468. String xyears = first + "-" + xlast;
  469. if (first.equals(last)) {
  470. xyears = first;
  471. }
  472. writer.println(TOP);
  473. if (!lastIs2002) { // Xerox only
  474. writer.println(XEROX_PREFIX + xyears + XEROX_SUFFIX + ". ");
  475. } else if (size == 1) { // PARC only
  476. writer.println(PARC_ONLY);
  477. } else { // XEROX plus PARC
  478. writer.println(XEROX_PREFIX + xyears + XEROX_SUFFIX + ", ");
  479. writer.println(PARC);
  480. }
  481. writeRest(writer);
  482. }
  483. }
  484. void writeRest(PrintWriter writer) {
  485. writer.println(" * All rights reserved. ");
  486. writer.println(" * This program and the accompanying materials are made available ");
  487. writer.println(" * under the terms of the Eclipse Public License v1.0 ");
  488. writer.println(" * which accompanies this distribution and is available at ");
  489. writer.println(" * http://www.eclipse.org/legal/epl-v10.html ");
  490. writer.println(" * ");
  491. writer.println(" * Contributors: ");
  492. writer.println(" * Xerox/PARC initial implementation ");
  493. writer.println(" * ******************************************************************/");
  494. writer.println("");
  495. }
  496. public static final String TOP = "/* *******************************************************************";
  497. public static final String PARC = " * 2002 Palo Alto Research Center, Incorporated (PARC).";
  498. public static final String PARC_ONLY = " * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).";
  499. public static final String XEROX_PREFIX = " * Copyright (c) ";
  500. public static final String XEROX_SUFFIX = " Xerox Corporation";
  501. /*
  502. * /* ******************************************************************* Copyright (c) 1998-2001 Xerox Corporation, 2002 Palo
  503. * Alto Research Center, Incorporated (PARC). All rights reserved. This program and the accompanying materials are made
  504. * available under the terms of the Eclipse Public License v1.0 which accompanies this distribution and is available at
  505. * http://www.eclipse.org/legal/epl-v10.html
  506. *
  507. * Contributors: Xerox/PARC initial implementation ******************************************************************
  508. */
  509. }
  510. /**
  511. * header search/replace using hokey string matching
  512. */
  513. class Header {
  514. /** replace the header in file */
  515. public static boolean replaceHeader(File file, HeaderInfo info) {
  516. // ArrayList years = new ArrayList();
  517. // int endLine = 0;
  518. BufferedReader input = null;
  519. PrintWriter output = null;
  520. FileWriter outWriter = null;
  521. int lineNum = 0;
  522. boolean result = false;
  523. final File inFile = new File(file.getPath() + ".tmp");
  524. try {
  525. File outFile = new File(file.getPath());
  526. if (!file.renameTo(inFile) || !inFile.canRead()) {
  527. throw new Error("unable to rename " + file + " to " + inFile);
  528. }
  529. outWriter = new FileWriter(outFile);
  530. input = new BufferedReader(new FileReader(inFile));
  531. output = new PrintWriter(outWriter, true);
  532. info.writeHeader(output);
  533. String line;
  534. while (null != (line = input.readLine())) {
  535. lineNum++;
  536. if (lineNum > info.lastLine) {
  537. output.println(line);
  538. }
  539. }
  540. } catch (IOException e) {
  541. System.err.println("writing line " + lineNum + " of " + file);
  542. e.printStackTrace(System.err);
  543. result = false;
  544. } finally {
  545. if (null != input) {
  546. try {
  547. input.close();
  548. } catch (IOException e) {
  549. result = false;
  550. }
  551. }
  552. if (null != outWriter) {
  553. try {
  554. outWriter.close();
  555. } catch (IOException e) {
  556. result = false;
  557. }
  558. }
  559. result = inFile.delete();
  560. }
  561. return result;
  562. }
  563. public static HeaderInfo checkFile(final File file) {
  564. ArrayList<String> years = new ArrayList<String>();
  565. int endLine = 0;
  566. BufferedReader input = null;
  567. int lineNum = 0;
  568. try {
  569. input = new BufferedReader(new FileReader(file));
  570. String line;
  571. while (null != (line = input.readLine())) {
  572. lineNum++;
  573. String ll = line.trim();
  574. if (ll.startsWith("package ") || ll.startsWith("import ")) {
  575. break; // ignore default package w/o imports
  576. }
  577. if (checkLine(line, years)) {
  578. endLine = lineNum;
  579. break;
  580. }
  581. }
  582. } catch (IOException e) {
  583. System.err.println("reading line " + lineNum + " of " + file);
  584. e.printStackTrace(System.err);
  585. } finally {
  586. if (null != input) {
  587. try {
  588. input.close();
  589. } catch (IOException e) {
  590. } // ignore
  591. }
  592. }
  593. return new HeaderInfo(file, endLine, years, endLine > 0);
  594. }
  595. /**
  596. * Add any years found (as String) to years, and return true at the first end-of-comment
  597. *
  598. * @return true if this line has end-of-comment
  599. */
  600. private static boolean checkLine(String line, ArrayList<String> years) {
  601. if ((null == line) || (0 == line.length())) {
  602. return false;
  603. }
  604. int loc;
  605. int start = 0;
  606. while ((-1 != (loc = line.indexOf("199", start)) || (-1 != (loc = line.indexOf("200", start))))) {
  607. char c = line.charAt(loc + 3);
  608. if ((c <= '9') && (c >= '0')) {
  609. years.add(line.substring(loc, loc + 4));
  610. }
  611. start = loc + 4;
  612. }
  613. return (-1 != line.indexOf("*/"));
  614. }
  615. } // class Header