PageRenderTime 75ms CodeModel.GetById 57ms app.highlight 15ms RepoModel.GetById 1ms app.codeStats 0ms

/JSMin/src/net/matthaynes/jsmin/JSMin.java

http://jsmin-ant-task.googlecode.com/
Java | 356 lines | 242 code | 34 blank | 80 comment | 38 complexity | 9b5879d993a0aae633b97cddf19dcf87 MD5 | raw file
  1/**
  2 * License Agreement.
  3 *
  4 * Ajax4jsf 1.1 - Natural Ajax for Java Server Faces (JSF)
  5 *
  6 * Copyright (C) 2007 Exadel, Inc.
  7 *
  8 * This library is free software; you can redistribute it and/or
  9 * modify it under the terms of the GNU Lesser General Public
 10 * License version 2.1 as published by the Free Software Foundation.
 11 *
 12 * This library 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 library; if not, write to the Free Software
 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 20 */
 21
 22/*
 23 * JSMin.java 2006-02-13
 24 * 
 25 * Copyright (c) 2006 John Reilly (www.inconspicuous.org)
 26 * 
 27 * This work is a translation from C to Java of jsmin.c published by
 28 * Douglas Crockford.  Permission is hereby granted to use the Java 
 29 * version under the same conditions as the jsmin.c on which it is
 30 * based.  
 31 * 
 32 * 
 33 * 
 34 * 
 35 * jsmin.c 2003-04-21
 36 * 
 37 * Copyright (c) 2002 Douglas Crockford (www.crockford.com)
 38 * 
 39 * Permission is hereby granted, free of charge, to any person obtaining a copy
 40 * of this software and associated documentation files (the "Software"), to deal
 41 * in the Software without restriction, including without limitation the rights
 42 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 43 * copies of the Software, and to permit persons to whom the Software is
 44 * furnished to do so, subject to the following conditions:
 45 * 
 46 * The above copyright notice and this permission notice shall be included in
 47 * all copies or substantial portions of the Software.
 48 * 
 49 * The Software shall be used for Good, not Evil.
 50 * 
 51 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 52 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 53 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 54 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 55 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 56 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 57 * SOFTWARE.
 58 */
 59
 60package net.matthaynes.jsmin;
 61import java.io.FileInputStream;
 62import java.io.FileNotFoundException;
 63import java.io.IOException;
 64import java.io.InputStream;
 65import java.io.OutputStream;
 66import java.io.PushbackInputStream;
 67
 68
 69
 70
 71public class JSMin {
 72	private static final int EOF = -1;
 73
 74	private PushbackInputStream in;
 75	private OutputStream out;
 76
 77	private int theA;
 78	private int theB;
 79	
 80	private int line;
 81	
 82	private int column;
 83	
 84	public JSMin(InputStream in, OutputStream out) {
 85		this.in = new PushbackInputStream(in);
 86		this.out = out;
 87		this.line = 0;
 88		this.column = 0;
 89	}
 90
 91	/**
 92	 * isAlphanum -- return true if the character is a letter, digit,
 93	 * underscore, dollar sign, or non-ASCII character.
 94	 */
 95	static boolean isAlphanum(int c) {
 96		return ( (c >= 'a' && c <= 'z') || 
 97				 (c >= '0' && c <= '9') || 
 98				 (c >= 'A' && c <= 'Z') || 
 99				 c == '_' || 
100				 c == '$' || 
101				 c == '\\' || 
102				 c > 126);
103	}
104
105	/**
106	 * get -- return the next character from stdin. Watch out for lookahead. If
107	 * the character is a control character, translate it to a space or
108	 * linefeed.
109	 */
110	int get() throws IOException {
111		int c = in.read();
112		
113		if(c == '\n'){
114			line++;
115			column = 0;
116		} else {
117			column++;
118		}
119
120		if (c >= ' ' || c == '\n' || c == EOF) {
121			return c;
122		}
123
124		if (c == '\r') {
125			column = 0;
126			return '\n';
127		}
128		
129		return ' ';
130	}
131
132	
133	
134	/**
135	 * Get the next character without getting it.
136	 */
137	int peek() throws IOException {
138		int lookaheadChar = in.read();
139		in.unread(lookaheadChar);
140		return lookaheadChar;
141	}
142
143	/**
144	 * next -- get the next character, excluding comments. peek() is used to see
145	 * if a '/' is followed by a '/' or '*'.
146	 */
147	int next() throws IOException, UnterminatedCommentException {
148		int c = get();
149		if (c == '/') {
150			switch (peek()) {
151			case '/':
152				for (;;) {
153					c = get();
154					if (c <= '\n') {
155						return c;
156					}
157				}
158
159			case '*':
160				get();
161				for (;;) {
162					switch (get()) {
163					case '*':
164						if (peek() == '/') {
165							get();
166							return ' ';
167						}
168						break;
169					case EOF:
170						throw new UnterminatedCommentException(line,column);
171					}
172				}
173
174			default:
175				return c;
176			}
177
178		}
179		return c;
180	}
181
182	/**
183	 * action -- do something! What you do is determined by the argument: 1
184	 * Output A. Copy B to A. Get the next B. 2 Copy B to A. Get the next B.
185	 * (Delete A). 3 Get the next B. (Delete B). action treats a string as a
186	 * single character. Wow! action recognizes a regular expression if it is
187	 * preceded by ( or , or =.
188	 */
189
190	void action(int d) throws IOException, UnterminatedRegExpLiteralException,
191			UnterminatedCommentException, UnterminatedStringLiteralException {
192		switch (d) {
193		case 1:
194			out.write(theA);
195		case 2:
196			theA = theB;
197
198			if (theA == '\'' || theA == '"') {
199				for (;;) {
200					out.write(theA);
201					theA = get();
202					if (theA == theB) {
203						break;
204					}
205					if (theA <= '\n') {
206						throw new UnterminatedStringLiteralException(line,column);
207					}
208					if (theA == '\\') {
209						out.write(theA);
210						theA = get();
211					}
212				}
213			}
214			
215		case 3:
216			theB = next();
217			if (theB == '/' && (theA == '(' || theA == ',' || theA == '=' ||
218                    			theA == ':' || theA == '[' || theA == '!' || 
219                    			theA == '&' || theA == '|' || theA == '?' || 
220                    			theA == '{' || theA == '}' || theA == ';' || 
221                    			theA == '\n')) {
222               
223				out.write(theA);
224				out.write(theB);
225				for (;;) {
226					theA = get();
227					if (theA == '/') {
228						break;
229					} else if (theA == '\\') {
230						out.write(theA);
231						theA = get();
232					} else if (theA <= '\n') {
233						throw new UnterminatedRegExpLiteralException(line,column);
234					}
235					out.write(theA);
236				}
237				theB = next();
238			}
239		}
240	}
241
242	/**
243	 * jsmin -- Copy the input to the output, deleting the characters which are
244	 * insignificant to JavaScript. Comments will be removed. Tabs will be
245	 * replaced with spaces. Carriage returns will be replaced with linefeeds.
246	 * Most spaces and linefeeds will be removed.
247	 */
248	public void jsmin() throws IOException, UnterminatedRegExpLiteralException, UnterminatedCommentException, UnterminatedStringLiteralException{
249		theA = '\n';
250		action(3);
251		while (theA != EOF) {
252			switch (theA) {
253			case ' ':
254				if (isAlphanum(theB)) {
255					action(1);
256				} else {
257					action(2);
258				}
259				break;
260			case '\n':
261				switch (theB) {
262				case '{':
263				case '[':
264				case '(':
265				case '+':
266				case '-':
267					action(1);
268					break;
269				case ' ':
270					action(3);
271					break;
272				default:
273					if (isAlphanum(theB)) {
274						action(1);
275					} else {
276						action(2);
277					}
278				}
279				break;
280			default:
281				switch (theB) {
282				case ' ':
283					if (isAlphanum(theA)) {
284						action(1);
285						break;
286					}
287					action(3);
288					break;
289				case '\n':
290					switch (theA) {
291					case '}':
292					case ']':
293					case ')':
294					case '+':
295					case '-':
296					case '"':
297					case '\'':
298						action(1);
299						break;
300					default:
301						if (isAlphanum(theA)) {
302							action(1);
303						} else {
304							action(3);
305						}
306					}
307					break;
308				default:
309					action(1);
310					break;
311				}
312			}
313		}
314		out.flush();
315	}
316
317	static class UnterminatedCommentException extends Exception {
318		public UnterminatedCommentException(int line,int column) {
319			super("Unterminated comment at line "+line+" and column "+column);
320		}
321	}
322
323	static class UnterminatedStringLiteralException extends Exception {
324		public UnterminatedStringLiteralException(int line,int column) {
325			super("Unterminated string literal at line "+line+" and column "+column);
326		}
327	}
328
329	static class UnterminatedRegExpLiteralException extends Exception {
330		public UnterminatedRegExpLiteralException(int line,int column) {
331			super("Unterminated regular expression at line "+line+" and column "+column);
332		}
333	}
334
335	public static void main(String arg[]) {
336		try {
337			JSMin jsmin = new JSMin(new FileInputStream(arg[0]), System.out);
338			jsmin.jsmin();
339		} catch (FileNotFoundException e) {
340			e.printStackTrace();
341		} catch (ArrayIndexOutOfBoundsException e) {
342			e.printStackTrace();			
343		} catch (IOException e) {
344			e.printStackTrace();
345		} catch (UnterminatedRegExpLiteralException e) {
346			e.printStackTrace();
347		} catch (UnterminatedCommentException e) {
348			e.printStackTrace();
349		} catch (UnterminatedStringLiteralException e) {
350			e.printStackTrace();
351		}
352	}
353
354
355
356}