/* * * Copyright (c) 2013 - 2017 Lijun Liao * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License version 3 * as published by the Free Software Foundation with the addition of the * following permission added to Section 15 as permitted in Section 7(a): * * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY * THE AUTHOR LIJUN LIAO. LIJUN LIAO DISCLAIMS THE WARRANTY OF NON INFRINGEMENT * OF THIRD PARTY RIGHTS. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * The interactive user interfaces in modified source and object code versions * of this program must display Appropriate Legal Notices, as required under * Section 5 of the GNU Affero General Public License. * * You can be released from the requirements of the license by purchasing * a commercial license. Buying such a license is mandatory as soon as you * develop commercial activities involving the XiPKI software without * disclosing the source code of your own applications. * * For more information, please contact Lijun Liao at this * address: lijun.liao@gmail.com */ package org.xipki.commons.common.test; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import org.xipki.commons.common.util.IoUtil; import org.xipki.commons.common.util.StringUtil; /** * @author Lijun Liao * @since 2.0.0 */ public class CanonicalizeCode { private final String baseDir; private final int baseDirLen; private CanonicalizeCode(String baseDir) { this.baseDir = baseDir.endsWith(File.separator) ? baseDir : baseDir + File.separator; this.baseDirLen = this.baseDir.length(); } public static void main(final String[] args) { try { String baseDir = args[0]; CanonicalizeCode canonicalizer = new CanonicalizeCode(baseDir); canonicalizer.canonicalize(); canonicalizer.checkWarnings(); } catch (Exception ex) { ex.printStackTrace(); } } private void canonicalize() throws Exception { canonicalizeDir(new File(baseDir)); } private void canonicalizeDir(final File dir) throws Exception { File[] files = dir.listFiles(); if (files == null) { return; } for (File file : files) { String filename = file.getName(); if (file.isDirectory()) { if (!"target".equals(filename) && !"tbd".equals(filename)) { canonicalizeDir(file); } } else { int idx = filename.lastIndexOf('.'); String extension = (idx == -1) ? filename : filename.substring(idx + 1); extension = extension.toLowerCase(); if ("java".equals(extension)) { canonicalizeFile(file); } } } } // method canonicalizeDir private void canonicalizeFile(final File file) throws Exception { byte[] newLine = detectNewline(file); BufferedReader reader = new BufferedReader(new FileReader(file)); ByteArrayOutputStream writer = new ByteArrayOutputStream(); try { String line; boolean skip = true; boolean lastLineEmpty = false; boolean licenseTextAdded = false; boolean thirdparty = false; int lineNumber = 0; while ((line = reader.readLine()) != null) { if (lineNumber == 0 && line.startsWith("// #THIRDPARTY#")) { thirdparty = true; skip = false; } lineNumber++; if (line.trim().startsWith("package ") || line.trim().startsWith("import ")) { if (!licenseTextAdded) { if (!thirdparty) { writeLicenseHeader(writer, newLine); } licenseTextAdded = true; } skip = false; } if (skip) { continue; } String canonicalizedLine = canonicalizeLine(line); boolean addThisLine = true; if (canonicalizedLine.isEmpty()) { if (!lastLineEmpty) { lastLineEmpty = true; } else { addThisLine = false; } } else { lastLineEmpty = false; } if (addThisLine) { writeLine(writer, newLine, canonicalizedLine); } } // end while } finally { writer.close(); reader.close(); } byte[] oldBytes = IoUtil.read(file); byte[] newBytes = writer.toByteArray(); if (!Arrays.equals(oldBytes, newBytes)) { File newFile = new File(file.getPath() + "-new"); IoUtil.save(file, newBytes); newFile.renameTo(file); System.out.println(file.getPath().substring(baseDirLen)); } } // method canonicalizeJavaFile private void checkWarnings() throws Exception { checkWarningsInDir(new File(baseDir)); } private void checkWarningsInDir(final File dir) throws Exception { File[] files = dir.listFiles(); if (files == null) { return; } for (File file : files) { if (file.isDirectory()) { if (!file.getName().equals("target") && !file.getName().equals("tbd")) { checkWarningsInDir(file); } continue; } else { String filename = file.getName(); int idx = filename.lastIndexOf('.'); String extension = (idx == -1) ? filename : filename.substring(idx + 1); extension = extension.toLowerCase(); if ("java".equals(extension)) { checkWarningsInFile(file); } } } } // method checkWarningsInDir private void checkWarningsInFile(final File file) throws Exception { if (file.getName().equals("package-info.java")) { return; } BufferedReader reader = new BufferedReader(new FileReader(file)); boolean authorsLineAvailable = false; boolean thirdparty = false; List<Integer> lineNumbers = new LinkedList<>(); int lineNumber = 0; try { String line; while ((line = reader.readLine()) != null) { lineNumber++; if (lineNumber == 1 && line.startsWith("// #THIRDPARTY")) { return; } if (!authorsLineAvailable && line.contains("* @author")) { authorsLineAvailable = true; } if (line.length() > 100 && !line.contains("http")) { lineNumbers.add(lineNumber); } } // end while } finally { reader.close(); } if (!lineNumbers.isEmpty()) { System.out.println("Please check file " + file.getPath().substring(baseDirLen) + ": lines " + Arrays.toString(lineNumbers.toArray(new Integer[0]))); } if (!authorsLineAvailable && !thirdparty) { System.out.println("Please check file " + file.getPath().substring(baseDirLen) + ": no authors line"); } } // method checkWarningsInJavaFile /** * replace tab by 4 spaces, delete white spaces at the end. */ private static String canonicalizeLine(final String line) { if (line.trim().startsWith("//")) { // comments String nline = line.replace("\t", " "); return removeTrailingSpaces(nline); } StringBuilder sb = new StringBuilder(); int len = line.length(); int lastNonSpaceCharIndex = 0; int index = 0; for (int i = 0; i < len; i++) { char ch = line.charAt(i); if (ch == '\t') { sb.append(" "); index += 4; } else if (ch == ' ') { sb.append(ch); index++; } else { sb.append(ch); index++; lastNonSpaceCharIndex = index; } } int numSpacesAtEnd = sb.length() - lastNonSpaceCharIndex; if (numSpacesAtEnd > 0) { sb.delete(lastNonSpaceCharIndex, sb.length()); } String ret = sb.toString(); if (ret.startsWith(" throws ")) { ret = " " + ret; } else if (ret.startsWith(" throws ")) { ret = " " + ret; } return ret; } // end canonicalizeJavaLine private static String removeTrailingSpaces(final String line) { final int n = line.length(); int idx; for (idx = n - 1; idx >= 0; idx--) { char ch = line.charAt(idx); if (ch != ' ') { break; } } return (idx == n - 1) ? line : line.substring(0, idx + 1); } // method removeTrailingSpaces private static byte[] detectNewline(File file) throws IOException { InputStream is = new FileInputStream(file); byte[] bytes = new byte[200]; int size; try { size = is.read(bytes); } finally { is.close(); } for (int i = 0; i < size - 1; i++) { byte bb = bytes[i]; if (bb == '\n') { return new byte[]{'\n'}; } else if (bb == '\r') { if (bytes[i + 1] == '\n') { return new byte[]{'\r', '\n'}; } else { return new byte[]{'\r'}; } } } return new byte[]{'\n'}; } private static void writeLicenseHeader(OutputStream out, byte[] newLine) throws IOException { writeLine(out, newLine, "/*"); writeLine(out, newLine, " *"); writeLine(out, newLine, " * Copyright (c) 2013 - 2017 Lijun Liao"); writeLine(out, newLine, " *"); writeLine(out, newLine, " * This program is free software; you can redistribute it and/or modify"); writeLine(out, newLine, " * it under the terms of the GNU Affero General Public License version 3"); writeLine(out,newLine, " * as published by the Free Software Foundation with the addition of the"); writeLine(out,newLine, " * following permission added to Section 15 as permitted in Section 7(a):"); writeLine(out, newLine, " *"); writeLine(out, newLine, " * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY"); writeLine(out, newLine, " * THE AUTHOR LIJUN LIAO. LIJUN LIAO DISCLAIMS THE WARRANTY OF NON INFRINGEMENT"); writeLine(out, newLine, " * OF THIRD PARTY RIGHTS."); writeLine(out, newLine, " *"); writeLine(out, newLine, " * This program is distributed in the hope that it will be useful,"); writeLine(out, newLine, " * but WITHOUT ANY WARRANTY; without even the implied warranty of"); writeLine(out, newLine, " * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the"); writeLine(out, newLine, " * GNU Affero General Public License for more details."); writeLine(out, newLine, " *"); writeLine(out, newLine, " * You should have received a copy of the GNU Affero General Public License"); writeLine(out, newLine, " * along with this program. If not, see <http://www.gnu.org/licenses/>."); writeLine(out, newLine, " *"); writeLine(out, newLine, " * The interactive user interfaces in modified source and object code versions"); writeLine(out, newLine, " * of this program must display Appropriate Legal Notices, as required under"); writeLine(out, newLine, " * Section 5 of the GNU Affero General Public License."); writeLine(out, newLine, " *"); writeLine(out, newLine, " * You can be released from the requirements of the license by purchasing"); writeLine(out, newLine, " * a commercial license. Buying such a license is mandatory as soon as you"); writeLine(out, newLine, " * develop commercial activities involving the XiPKI software without"); writeLine(out, newLine, " * disclosing the source code of your own applications."); writeLine(out, newLine, " *"); writeLine(out, newLine, " * For more information, please contact Lijun Liao at this"); writeLine(out, newLine, " * address: lijun.liao@gmail.com"); writeLine(out, newLine, " */"); writeLine(out, newLine, ""); } private static void writeLine(OutputStream out, byte[] newLine, String line) throws IOException { if (StringUtil.isNotBlank(line)) { out.write(line.getBytes()); } out.write(newLine); } }