| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| * <p> |
| * Copyright (c) 2006 John Reilly (www.inconspicuous.org) This work is a |
| * translation from C to Java of jsmin.c published by Douglas Crockford. |
| * Permission is hereby granted to use the Java version under the same |
| * conditions as the jsmin.c on which it is based. |
| * <p> |
| * http://www.crockford.com/javascript/jsmin.html |
| */ |
| package org.apache.felix.configurator.impl.json; |
| |
| import java.io.IOException; |
| import java.io.PushbackReader; |
| import java.io.Reader; |
| import java.io.Writer; |
| |
| public class JSMin { |
| |
| private static final int EOF = -1; |
| |
| private final PushbackReader in; |
| |
| private final Writer out; |
| |
| private int theA; |
| |
| private int theB; |
| |
| private int theLookahead = EOF; |
| |
| private int theX = EOF; |
| |
| private int theY = EOF; |
| |
| public JSMin(final Reader in, final Writer out) { |
| this.in = new PushbackReader(in); |
| this.out = out; |
| } |
| |
| /** |
| * isAlphanum -- return true if the character is a letter, digit, underscore, |
| * dollar sign, or non-ASCII character. |
| */ |
| private boolean isAlphanum(final int c) { |
| return ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') |
| || (c >= 'A' && c <= 'Z') || c == '_' || c == '$' || c == '\\' || c > 126); |
| } |
| |
| /** |
| * get -- return the next character from stdin. Watch out for lookahead. If |
| * the character is a control character, translate it to a space or |
| * linefeed. |
| */ |
| private int get() throws IOException { |
| int c = theLookahead; |
| theLookahead = EOF; |
| if ( c == EOF ) { |
| c = in.read(); |
| } |
| |
| if (c >= ' ' || c == '\n' || c == EOF) { |
| return c; |
| } |
| |
| if (c == '\r') { |
| return '\n'; |
| } |
| |
| return ' '; |
| } |
| |
| /** |
| * peek -- get the next character without getting it. |
| */ |
| private int peek() throws IOException { |
| theLookahead = get(); |
| return theLookahead; |
| } |
| |
| /** |
| * next -- get the next character, excluding comments. peek() is used to see |
| * if a '/' is followed by a '/' or '*'. |
| */ |
| private int next() throws IOException { |
| int c = get(); |
| if (c == '/') { |
| switch (peek()) { |
| case '/': |
| for (;;) { |
| c = get(); |
| if (c <= '\n') { |
| break; |
| } |
| } |
| break; |
| case '*': |
| get(); |
| while (c != ' ') { |
| switch (get()) { |
| case '*': |
| if (peek() == '/') { |
| get(); |
| c = ' '; |
| } |
| break; |
| case EOF: |
| throw new IOException("Unterminated comment."); |
| } |
| } |
| break; |
| } |
| |
| } |
| theY = theX; |
| theX = c; |
| return c; |
| } |
| |
| /** |
| * action -- do something! What you do is determined by the argument: |
| * <ul> |
| * <li>1 Output A. Copy B to A. Get the next B.</li> |
| * <li>2 Copy B to A. Get the next B. (Delete A).</li> |
| * <li>3 Get the next B. (Delete B).</li> |
| * </ul> |
| * action treats a string as a single character. Wow!<br/> |
| * action recognizes a regular expression if it is preceded by ( or , or =. |
| */ |
| void action(final int d) throws IOException { |
| switch (d) { |
| case 1: |
| out.write(theA); |
| if ((theY == '\n' || theY == ' ') && |
| (theA == '+' || theA == '-' || theA == '*' || theA == '/') && |
| (theB == '+' || theB == '-' || theB == '*' || theB == '/')) { |
| out.write(theY); |
| } |
| case 2: |
| theA = theB; |
| |
| if (theA == '\'' || theA == '"' || theA == '`') { |
| for (;;) { |
| out.write(theA); |
| theA = get(); |
| if (theA == theB) { |
| break; |
| } |
| if (theA == '\\') { |
| out.write(theA); |
| theA = get(); |
| } |
| if ( theA == EOF) { |
| throw new IOException("Unterminated string literal."); |
| } |
| } |
| } |
| |
| case 3: |
| theB = next(); |
| if (theB == '/' |
| && (theA == '(' || theA == ',' || theA == '=' || theA == ':' |
| || theA == '[' || theA == '!' || theA == '&' || theA == '|' |
| || theA == '?' || theA == '+' || theA == '-' || theA == '~' |
| || theA == '*' || theA == '/' || theA == '{' || theA == '\n')) { |
| out.write(theA); |
| if (theA == '/' || theA == '*') { |
| out.write(' '); |
| } |
| out.write(theB); |
| for (;;) { |
| theA = get(); |
| if (theA == '[') { |
| for (;;) { |
| out.write(theA); |
| theA = get(); |
| if (theA == ']') { |
| break; |
| } |
| if (theA == '\\') { |
| out.write(theA); |
| theA = get(); |
| } |
| if (theA == EOF) { |
| throw new IOException("Unterminated set in Regular Expression literal."); |
| } |
| } |
| } else if (theA == '/') { |
| switch (peek()) { |
| case '/': |
| case '*': |
| throw new IOException("Unterminated set in Regular Expression literal."); |
| } |
| break; |
| } else if (theA == '\\') { |
| out.write(theA); |
| theA = get(); |
| } else if (theA == EOF) { |
| throw new IOException("Unterminated Regular Expression literal."); |
| } |
| out.write(theA); |
| } |
| theB = next(); |
| } |
| } |
| } |
| |
| /** |
| * jsmin -- Copy the input to the output, deleting the characters which are |
| * insignificant to JavaScript. Comments will be removed. Tabs will be |
| * replaced with spaces. Carriage returns will be replaced with linefeeds. |
| * Most spaces and linefeeds will be removed. |
| */ |
| public void jsmin() throws IOException { |
| if (peek() == 0xEF) { |
| get(); |
| get(); |
| get(); |
| } |
| theA = '\n'; |
| action(3); |
| while (theA != EOF) { |
| switch (theA) { |
| case ' ': |
| action(isAlphanum(theB) ? 1: 2); |
| break; |
| case '\n': |
| switch (theB) { |
| case '{': |
| case '[': |
| case '(': |
| case '+': |
| case '-': |
| case '!': |
| case '~': |
| action(1); |
| break; |
| case ' ': |
| action(3); |
| break; |
| default: |
| action(isAlphanum(theB) ? 1: 2); |
| } |
| break; |
| default: |
| switch (theB) { |
| case ' ': |
| action(isAlphanum(theB) ? 1: 3); |
| break; |
| case '\n': |
| switch (theA) { |
| case '}': |
| case ']': |
| case ')': |
| case '+': |
| case '-': |
| case '"': |
| case '\'': |
| case '`': |
| action(1); |
| break; |
| default: |
| action(isAlphanum(theB) ? 1: 3); |
| } |
| break; |
| default: |
| action(1); |
| break; |
| } |
| } |
| } |
| out.flush(); |
| } |
| } |