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