content: /**
   * Copyright Alex Objelean
   */
  package ro.isdc.wro.model.resource.processor.support;
  
  import java.io.Writer;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.LinkedList;
  import java.util.List;
  import java.util.regex.PatternSyntaxException;
  
  import org.apache.commons.lang3.StringUtils;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  /**
   * Css minify barryvan implementation.
   */
  public class CSSMin {
    private static final Logger LOG = LoggerFactory.getLogger(CSSMin.class);
  
  
    public void formatFile(final String input, final Writer writer)
      throws Exception {
      try {
        final StringBuilder sb = new StringBuilder(input.replaceAll("[\t\n\r]", "").replaceAll("  ", " "));
        int k, n;
  
        // Find the start of the comment
        while ((n = sb.indexOf("/*")) != -1) {
          k = sb.indexOf("*/", n + 2);
          if (k == -1) {
            throw new Exception("Error: Unterminated comment.");
          }
          sb.delete(n, k + 2);
        }
  
        final List<Selector> selectors = new LinkedList<Selector>();
        n = 0;
        while ((k = sb.indexOf("}", n)) != -1) {
          try {
            selectors.add(new Selector(sb.substring(n, k + 1)));
          } catch (final Exception e) {
            // skip
          }
          n = k + 1;
        }
  
          for (Selector selector : selectors) {
              writer.write(selector.toString());
          }
        writer.write("\r\n");
        writer.flush();
      } catch (final Exception e) {
        LOG.error(e.getMessage(), e);
      }
  
    }
  }
  
  
  class Selector {
    private static final Logger LOG = LoggerFactory.getLogger(CSSMin.class);
    private final Property[] properties;
    private final String selector;
  
  
    /**
     * Creates a new Selector using the supplied strings.
     *
     * @param selector The selector; for example, "div { border: solid 1px red; color: blue; }"
     */
    public Selector(final String selector) throws Exception {
      final String[] parts = selector.split("\\{"); // We have to escape the { with a \ for the regex, which itself
      // requires escaping for the string. Sigh.
      if (parts.length < 2) {
        throw new Exception("Warning: Incomplete selector: " + selector);
      }
      this.selector = parts[0].trim();
      String contents = parts[1].trim();
      if (contents.length() <= 1) {
        throw new Exception("Warning: Empty selector body: " + selector);
      }
      if (contents.charAt(contents.length() - 1) != '}') { // Ensure that we have a leading and trailing brace.
        throw new Exception("Warning: Unterminated selector: " + selector);
      }
      contents = StringUtils.substringBefore(contents, "}");
      properties = parseProperties(contents);
      sortProperties(properties);
    }
  
  
    /**
     * Prints out this selector and its contents nicely, with the contents sorted alphabetically.
     */
    @Override
    public String toString() {
      final StringBuilder sb = new StringBuilder();
      sb.append(selector).append("{");
      for (final Property property : properties) {
        if (property != null) {
          sb.append(property.toString());
        }
      }
      sb.append("}");
      return sb.toString();
    }
  
  
    /**
     * Parses out the properties of a selector's body.
     *
     * @param contents The body; for example, "border: solid 1px red; color: blue;"
     */
    private Property[] parseProperties(final String contents) {
      final String[] parts = contents.split(";");
      final List<Property> resultsAsList = new ArrayList<Property>();
  
        for (String part : parts) {
            try {
                // ignore empty parts
                if (!StringUtils.isEmpty(part.trim())) {
                    resultsAsList.add(new Property(part));
                }
            } catch (final Exception e) {
                LOG.warn(e.getMessage(), e);
            }
        }
      return resultsAsList.toArray(new Property[resultsAsList.size()]);
    }
  
  
    private void sortProperties(final Property[] propertiesToSort) {
      Arrays.sort(propertiesToSort);
    }
  }
  
  
  class Property
    implements Comparable {
    private static final Logger LOG = LoggerFactory.getLogger(CSSMin.class);
    protected String property;
    protected Value[] values;
  
  
    /**
     * Creates a new Property using the supplied strings. Parses out the values of the property selector.
     *
     * @param property The property; for example, "border: solid 1px red;" or
     *        "-moz-box-shadow: 3px 3px 3px rgba(255, 255, 0, 0.5);".
     */
    public Property(final String property) throws Exception {
      try {
        // Parse the property.
        //final String[] parts = property.split(":"); // Split "color: red" to ["color", " red"]
        final List<String> parts = Arrays.asList(property.split(":", 2)); // Split "color: red" to ["color", " red"]
        final List<String> nonEmptyParts = new ArrayList<String>();
        for (String part : parts) {
          if (!StringUtils.isEmpty(part.trim())) {
            nonEmptyParts.add(part.trim());
          }
        }
        if (nonEmptyParts.size() < 2) {
          throw new Exception("Warning: Incomplete property: " + property);
        }
        this.property = nonEmptyParts.get(0).toLowerCase();
  
        values = parseValues(nonEmptyParts.get(1).replaceAll(", ", ","));
  
      } catch (final PatternSyntaxException e) {
        // Invalid regular expression used.
      }
    }
  
  
    /**
     * Prints out this property nicely, with the contents sorted in a standardised order.
     */
    @Override
    public String toString() {
      final StringBuilder sb = new StringBuilder();
      sb.append(property).append(":");
      for (final Value v : values) {
        sb.append(v.toString()).append(",");
      }
      sb.deleteCharAt(sb.length() - 1); // Delete the trailing comma.
      sb.append(";");
      return sb.toString();
    }
  
  
    public int compareTo(final Object other) {
      return property.compareTo(((Property)other).property);
    }
  
  
    private Value[] parseValues(final String contents) {
      final String[] parts = contents.split(",");
      final Value[] results = new Value[parts.length];
  
      for (int i = 0; i < parts.length; i++) {
        try {
          results[i] = new Value(parts[i]);
        } catch (final Exception e) {
          LOG.error(e.getMessage(), e);
          results[i] = null;
        }
      }
  
      return results;
    }
  }
  
  
  class Value {
  
    String[] parts;
  
  
    public Value(final String value) throws Exception {
      // Parse the value.
      parts = value.split(" "); // Split "solid 1px red" to ["solid","1px","red"] and sort them.
    }
  
  
    @Override
    public String toString() {
      final StringBuilder sb = new StringBuilder();
      for (final String part : parts) {
        sb.append(part).append(" ");
      }
      return sb.toString().trim();
    }
  }
  