View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   */
17  package org.apache.creadur.tentacles;
18  
19  import java.io.ByteArrayInputStream;
20  import java.io.File;
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.util.ArrayList;
24  import java.util.List;
25  
26  import org.codehaus.swizzle.stream.DelimitedTokenReplacementInputStream;
27  import org.codehaus.swizzle.stream.ExcludeFilterInputStream;
28  import org.codehaus.swizzle.stream.StringTokenHandler;
29  
30  /**
31   * Little utility that will yank the author comments from java files.
32   * 
33   * If the resulting comment block is effectively empty, it will be yanked too.
34   */
35  public class Deauthorize {
36  
37  	/**
38  	 * All input must be valid directories.
39  	 * 
40  	 * Invalid input is logged to System.err and skipped
41  	 * 
42  	 * @param args
43  	 *            a list of directories to scan and fix
44  	 * @throws Exception
45  	 *             in case of errors.
46  	 */
47  	public static void main(final String[] args) throws Exception {
48  
49  		if (args.length == 0) {
50  			throw new IllegalArgumentException(
51  					"At least one directory must be specified");
52  		}
53  
54  		final List<File> dirs = new ArrayList<File>();
55  
56  		// Check the input args upfront
57  		for (final String arg : args) {
58  			final File dir = new File(arg);
59  
60  			if (not(dir.exists(), "Does not exist: %s", arg)) {
61  				continue;
62  			}
63  			if (not(dir.isDirectory(), "Not a directory: %s", arg)) {
64  				continue;
65  			}
66  
67  			dirs.add(dir);
68  		}
69  
70  		// Exit if we got bad input
71  		if (dirs.size() != args.length) {
72  			System.exit(1);
73  		}
74  
75  		// Go!
76  		for (final File dir : dirs) {
77  			deauthorize(dir);
78  		}
79  	}
80  
81  	/**
82  	 * Iterate over all the java files in the given directory
83  	 * 
84  	 * Read in the file so we can guess the line ending -- if we didn't need to
85  	 * do that we could just stream. Run the content through Swizzle Stream and
86  	 * filter out any author tags as well as any comment blocks that wind up (or
87  	 * already were) empty as a result.
88  	 * 
89  	 * If that had any effect on the contents of the file, write it back out.
90  	 * 
91  	 * Should skip any files that are not readable or writable.
92  	 * 
93  	 * Will log an error on System.err for any files that were updated and were
94  	 * not writable. Files that are not writable and don't need updating are
95  	 * simply ignored.
96  	 * 
97  	 * @param dir
98  	 * @throws IOException
99  	 */
100 	private static void deauthorize(final File dir) throws IOException {
101 		deauthorize(dir, new IOSystem());
102 	}
103 
104 	private static void deauthorize(final File dir, final IOSystem io)
105 			throws IOException {
106 		for (final File file : new FileSystem().collect(dir, ".*\\.java")) {
107 
108 			if (not(file.canRead(), "File not readable: %s",
109 					file.getAbsolutePath())) {
110 				continue;
111 			}
112 
113 			final String text = io.slurp(file);
114 
115 			// You really can't trust text to be in the native line ending
116 			final String eol = text.contains("\r\n") ? "\r\n" : "\n";
117 			final String startComment = eol + "/*";
118 			final String endComment = "*/" + eol;
119 
120 			final InputStream baseIn = new ByteArrayInputStream(text.getBytes());
121 
122 			// Yank author tags
123 			InputStream in = new ExcludeFilterInputStream(baseIn, " * @author",
124 					eol);
125 
126 			// Clean "empty" comments
127 			in = new DelimitedTokenReplacementInputStream(in, startComment,
128 					endComment, new StringTokenHandler() {
129 						@Override
130 						public String handleToken(final String commentBlock)
131 								throws IOException {
132 
133 							// Yank if empty
134 							if (commentBlock.replaceAll("[\\s*]", "").length() == 0) {
135 								return eol;
136 							}
137 
138 							// Keep otherwise
139 							return startComment + commentBlock + endComment;
140 						}
141 					});
142 
143 			final byte[] content = io.read(in);
144 
145 			if (content.length != file.length()) {
146 
147 				if (not(file.canWrite(), "File not writable: %s",
148 						file.getAbsolutePath())) {
149 					continue;
150 				}
151 
152 				io.copy(content, file);
153 			}
154 		}
155 
156 	}
157 
158 	private static boolean not(boolean b, final String message,
159 			final Object... details) {
160 		b = !b;
161 		if (b) {
162 			System.err.printf(message, details);
163 			System.err.println();
164 		}
165 		return b;
166 	}
167 }