Projects >> wro4j >>5035b07bb50143de55a9d6b1c15b379277cd038e

Chunk
Conflicting content
package ro.isdc.wro.manager.factory.standalone;

<<<<<<< HEAD
=======
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

>>>>>>> b552441e99002d2ffd91ba6e5cc91e498e134570
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.Validate;
Solution content
  @Override
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ro.isdc.wro.config.Context;
import ro.isdc.wro.manager.factory.WroManagerFactory;
import ro.isdc.wro.model.factory.WroModelFactory;
import ro.isdc.wro.model.factory.XmlModelFactory;
import ro.isdc.wro.model.group.GroupExtractor;
import ro.isdc.wro.model.group.processor.GroupExtractorDecorator;
import ro.isdc.wro.model.resource.locator.ResourceLocator;
import ro.isdc.wro.model.resource.locator.factory.DefaultResourceLocatorFactory;
import ro.isdc.wro.model.resource.locator.factory.ResourceLocatorFactory;
import ro.isdc.wro.model.resource.locator.support.FileSystemResourceLocator;
import ro.isdc.wro.model.resource.locator.StandaloneServletContextUriLocator;
import ro.isdc.wro.model.resource.processor.factory.DefaultProcessorsFactory;
import ro.isdc.wro.model.resource.processor.factory.ProcessorsFactory;

/**
 * {@link WroManagerFactory} instance used by the maven plugin.
 *
 * @author Alex Objelean
 */
public class DefaultStandaloneContextAwareManagerFactory
  extends StandaloneWroManagerFactory implements StandaloneContextAware {
  private static final Logger LOG = LoggerFactory.getLogger(DefaultStandaloneContextAwareManagerFactory.class);
  /**
   * Context used by stand-alone process.
   */
  StandaloneContext standaloneContext;
  /**
   * {@inheritDoc}
   */
  public void initialize(final StandaloneContext standaloneContext) {
    Validate.notNull(standaloneContext);
    this.standaloneContext = standaloneContext;
    //This is important in order to make plugin aware about ignoreMissingResources option.
    Context.get().getConfig().setIgnoreMissingResources(standaloneContext.isIgnoreMissingResources());
    LOG.debug("initialize: {}", standaloneContext);
    LOG.debug("config: {}", Context.get().getConfig());
  }


  @Override
  protected GroupExtractor newGroupExtractor() {
    return new GroupExtractorDecorator(super.newGroupExtractor()) {
      @Override
      public boolean isMinimized(final HttpServletRequest request) {
        return standaloneContext.isMinimize();
      }
    };
  }


  @Override
  protected WroModelFactory newModelFactory() {
    return new XmlModelFactory() {
      @Override
      protected ResourceLocator getModelResourceLocator() {
        return new FileSystemResourceLocator(standaloneContext.getWroFile());
      }
    };
  }
  protected ProcessorsFactory newProcessorsFactory() {
    return new DefaultProcessorsFactory();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  protected ResourceLocatorFactory newLocatorFactory() {
    //TODO fix 
    return new StandaloneServletContextUriLocator(standaloneContext);
    return DefaultResourceLocatorFactory.standaloneFactory(standaloneContext.getContextFolder());
  }
}
File
DefaultStandaloneContextAwareManagerFactory.java
Developer's decision
Manual
Kind of conflict
Import
Chunk
Conflicting content
>>>>>>> b552441e99002d2ffd91ba6e5cc91e498e134570
import ro.isdc.wro.model.factory.XmlModelFactory;
import ro.isdc.wro.model.group.GroupExtractor;
import ro.isdc.wro.model.group.processor.GroupExtractorDecorator;
<<<<<<< HEAD
import ro.isdc.wro.model.resource.locator.ResourceLocator;
import ro.isdc.wro.model.resource.locator.factory.DefaultResourceLocatorFactory;
import ro.isdc.wro.model.resource.locator.factory.ResourceLocatorFactory;
import ro.isdc.wro.model.resource.locator.support.FileSystemResourceLocator;
=======
import ro.isdc.wro.model.resource.locator.ServletContextUriLocator;
import ro.isdc.wro.model.resource.locator.StandaloneServletContextUriLocator;
import ro.isdc.wro.model.resource.processor.factory.DefaultProcessorsFactory;
import ro.isdc.wro.model.resource.processor.factory.ProcessorsFactory;
Solution content
package ro.isdc.wro.manager.factory.standalone;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ro.isdc.wro.config.Context;
import ro.isdc.wro.manager.factory.WroManagerFactory;
import ro.isdc.wro.model.factory.WroModelFactory;
import ro.isdc.wro.model.factory.XmlModelFactory;
import ro.isdc.wro.model.group.GroupExtractor;
import ro.isdc.wro.model.group.processor.GroupExtractorDecorator;
import ro.isdc.wro.model.resource.locator.ResourceLocator;
import ro.isdc.wro.model.resource.locator.factory.DefaultResourceLocatorFactory;
import ro.isdc.wro.model.resource.locator.factory.ResourceLocatorFactory;
import ro.isdc.wro.model.resource.locator.support.FileSystemResourceLocator;
import ro.isdc.wro.model.resource.locator.StandaloneServletContextUriLocator;
import ro.isdc.wro.model.resource.processor.factory.DefaultProcessorsFactory;
import ro.isdc.wro.model.resource.processor.factory.ProcessorsFactory;

/**
 * {@link WroManagerFactory} instance used by the maven plugin.
 *
 * @author Alex Objelean
 */
public class DefaultStandaloneContextAwareManagerFactory
  extends StandaloneWroManagerFactory implements StandaloneContextAware {
  private static final Logger LOG = LoggerFactory.getLogger(DefaultStandaloneContextAwareManagerFactory.class);
  /**
   * Context used by stand-alone process.
   */
  StandaloneContext standaloneContext;
  /**
   * {@inheritDoc}
   */
  public void initialize(final StandaloneContext standaloneContext) {
    Validate.notNull(standaloneContext);
    this.standaloneContext = standaloneContext;
    //This is important in order to make plugin aware about ignoreMissingResources option.
    Context.get().getConfig().setIgnoreMissingResources(standaloneContext.isIgnoreMissingResources());
    LOG.debug("initialize: {}", standaloneContext);
    LOG.debug("config: {}", Context.get().getConfig());
  }


  @Override
  protected GroupExtractor newGroupExtractor() {
    return new GroupExtractorDecorator(super.newGroupExtractor()) {
      @Override
      public boolean isMinimized(final HttpServletRequest request) {
        return standaloneContext.isMinimize();
      }
    };
  }


  @Override
  protected WroModelFactory newModelFactory() {
    return new XmlModelFactory() {
      @Override
      protected ResourceLocator getModelResourceLocator() {
        return new FileSystemResourceLocator(standaloneContext.getWroFile());
      }
    };
  }

  @Override
  protected ProcessorsFactory newProcessorsFactory() {
    return new DefaultProcessorsFactory();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  protected ResourceLocatorFactory newLocatorFactory() {
    //TODO fix 
    return new StandaloneServletContextUriLocator(standaloneContext);
    return DefaultResourceLocatorFactory.standaloneFactory(standaloneContext.getContextFolder());
  }
}
File
DefaultStandaloneContextAwareManagerFactory.java
Developer's decision
Manual
Kind of conflict
Import
Chunk
Conflicting content
   * {@inheritDoc}
   */
  @Override
<<<<<<< HEAD
  protected ResourceLocatorFactory newLocatorFactory() {
    return DefaultResourceLocatorFactory.standaloneFactory(standaloneContext.getContextFolder());
=======
  protected ServletContextUriLocator newServletContextUriLocator() {
    return new StandaloneServletContextUriLocator(standaloneContext);
>>>>>>> b552441e99002d2ffd91ba6e5cc91e498e134570
  }
}
Solution content
package ro.isdc.wro.manager.factory.standalone;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ro.isdc.wro.config.Context;
import ro.isdc.wro.manager.factory.WroManagerFactory;
import ro.isdc.wro.model.factory.WroModelFactory;
import ro.isdc.wro.model.factory.XmlModelFactory;
import ro.isdc.wro.model.group.GroupExtractor;
import ro.isdc.wro.model.group.processor.GroupExtractorDecorator;
import ro.isdc.wro.model.resource.locator.ResourceLocator;
import ro.isdc.wro.model.resource.locator.factory.DefaultResourceLocatorFactory;
import ro.isdc.wro.model.resource.locator.factory.ResourceLocatorFactory;
import ro.isdc.wro.model.resource.locator.support.FileSystemResourceLocator;
import ro.isdc.wro.model.resource.locator.StandaloneServletContextUriLocator;
import ro.isdc.wro.model.resource.processor.factory.DefaultProcessorsFactory;
import ro.isdc.wro.model.resource.processor.factory.ProcessorsFactory;

/**
 * {@link WroManagerFactory} instance used by the maven plugin.
 *
 * @author Alex Objelean
 */
public class DefaultStandaloneContextAwareManagerFactory
  extends StandaloneWroManagerFactory implements StandaloneContextAware {
  private static final Logger LOG = LoggerFactory.getLogger(DefaultStandaloneContextAwareManagerFactory.class);
  /**
   * Context used by stand-alone process.
   */
  StandaloneContext standaloneContext;
  /**
   * {@inheritDoc}
   */
  public void initialize(final StandaloneContext standaloneContext) {
    Validate.notNull(standaloneContext);
    this.standaloneContext = standaloneContext;
    //This is important in order to make plugin aware about ignoreMissingResources option.
    Context.get().getConfig().setIgnoreMissingResources(standaloneContext.isIgnoreMissingResources());
    LOG.debug("initialize: {}", standaloneContext);
    LOG.debug("config: {}", Context.get().getConfig());
  }


  @Override
  protected GroupExtractor newGroupExtractor() {
    return new GroupExtractorDecorator(super.newGroupExtractor()) {
      @Override
      public boolean isMinimized(final HttpServletRequest request) {
        return standaloneContext.isMinimize();
      }
    };
  }


  @Override
  protected WroModelFactory newModelFactory() {
    return new XmlModelFactory() {
      @Override
      protected ResourceLocator getModelResourceLocator() {
        return new FileSystemResourceLocator(standaloneContext.getWroFile());
      }
    };
  }

  @Override
  protected ProcessorsFactory newProcessorsFactory() {
    return new DefaultProcessorsFactory();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  protected ResourceLocatorFactory newLocatorFactory() {
    //TODO fix 
    return new StandaloneServletContextUriLocator(standaloneContext);
    return DefaultResourceLocatorFactory.standaloneFactory(standaloneContext.getContextFolder());
  }
}
File
DefaultStandaloneContextAwareManagerFactory.java
Developer's decision
Manual
Kind of conflict
Method invocation
Method signature
Return statement
Chunk
Conflicting content
  }

  /**
    return pathInfo;
  }

  /**
   * 

} return false; } /** } } /** <<<<<<< HEAD /* * Copyright (c) 2008. All rights reserved. */ package ro.isdc.wro.util; import static org.apache.commons.lang3.Validate.notNull; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.io.Writer; import java.util.Enumeration; import java.util.Locale; import java.util.Properties; import java.util.TimeZone; import java.util.UUID; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.servlet.http.HttpServletRequest; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.time.FastDateFormat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ro.isdc.wro.WroRuntimeException; import ro.isdc.wro.model.WroModel; import ro.isdc.wro.model.factory.WroModelFactory; /** * Utility class. * * @author Alex Objelean * @created Created on Nov 13, 2008 */ public final class WroUtil { private static final Logger LOG = LoggerFactory.getLogger(WroUtil.class); /** * Empty line pattern. */ public static final Pattern EMTPY_LINE_PATTERN = Pattern.compile(loadRegexpWithKey("emptyLine"), Pattern.MULTILINE); /** * Thread safe date format used to transform milliseconds into date as string to put in response header. The localy is * set explicitly to US to conform to specification. */ private static final FastDateFormat DATE_FORMAT = FastDateFormat.getInstance("E, dd MMM yyyy HH:mm:ss z", TimeZone.getTimeZone("GMT"), Locale.US); /** * Patterns used to search for mangled Accept-Encoding header. */ private static final Pattern PATTERN_ACCEPT_ENCODING = Pattern.compile(loadRegexpWithKey("requestHeader.acceptEncoding")); private static final Pattern PATTERN_GZIP = Pattern.compile(loadRegexpWithKey("requestHeader.gzip")); private static final AtomicInteger threadFactoryNumber = new AtomicInteger(1); public static final InputStream EMPTY_STREAM = new ByteArrayInputStream("".getBytes()); /** * @return {@link ThreadFactory} with daemon threads. */ public static ThreadFactory createDaemonThreadFactory(final String name) { return new ThreadFactory() { private final String prefix = "wro4j-" + name + "-" + threadFactoryNumber.getAndIncrement() + "-thread-"; private final AtomicInteger threadNumber = new AtomicInteger(1); public Thread newThread(final Runnable runnable) { final Thread thread = new Thread(runnable, prefix + threadNumber.getAndIncrement()); thread.setDaemon(true); return thread; } }; } /** * to transform * @return string representation of the date. * StringUtils.startsWithIgnoreCase("abc", "abcdef") = true * StringUtils.startsWithIgnoreCase("abc", "ABCDEF") = true */ *

* * Transforms milliseconds into date format for response header of this form: Sat, 10 Apr 2010 17:31:31 GMT. * * @param milliseconds * * @param location public static String toDateAsString(final long milliseconds) { return DATE_FORMAT.format(milliseconds); } /** * Retrieve pathInfo from a given location. * * @param request * @param location * where to search contextPath. * @return pathInfo value. */ public static String getPathInfoFromLocation(final HttpServletRequest request, final String location) { if (StringUtils.isEmpty(location)) { throw new IllegalArgumentException("Location cannot be empty string!"); } final String contextPath = request.getContextPath(); if (contextPath != null) { if (startsWithIgnoreCase(location, contextPath)) { return location.substring(contextPath.length()); } else { return location; } } final String noSlash = location.substring(1); final int nextSlash = noSlash.indexOf('/'); if (nextSlash == -1) { return ""; } final String pathInfo = noSlash.substring(nextSlash); return pathInfo; } /** *

* Case insensitive check if a String starts with a specified prefix. *

*

* nulls are handled without exceptions. Two null references are considered to be equal. The * comparison is case insensitive. *

* *
   * StringUtils.startsWithIgnoreCase(null, null)      = true
   * StringUtils.startsWithIgnoreCase(null, "abcdef")  = false
   * StringUtils.startsWithIgnoreCase("abc", null)     = false
   * @see java.lang.String#startsWith(String)
   * @param str
   *          the String to check, may be null
   * @param prefix
   *          the prefix to find, may be null
   * @return true if the String starts with the prefix, case insensitive, or both null
   */
  public static boolean startsWithIgnoreCase(final String str, final String prefix) {
    return startsWith(str, prefix, true);
  }

  /**
   * Creates a folder like implementation for a class. Ex: com.mycompany.MyClass -> com/mycompany/
   *
   * @param clazz
   *          used as a base location for determining the package path.
   * @return a string representation of the path where the class resides.
   */
  public static String toPackageAsFolder(final Class clazz) {
    Validate.notNull(clazz, "Class cannot be null!");
    return clazz.getPackage().getName().replace('.', '/');
  }

  /**
   * 

* Check if a String starts with a specified prefix (optionally case insensitive). *

* * @see java.lang.String#startsWith(String) * @param str * the String to check, may be null * @param prefix * the prefix to find, may be null * @param ignoreCase * inidicates whether the compare should ignore case (case insensitive) or not. * @return true if the String starts with the prefix or both null */ private static boolean startsWith(final String str, final String prefix, final boolean ignoreCase) { if (str == null || prefix == null) { return (str == null && prefix == null); } if (prefix.length() > str.length()) { return false; } return str.regionMatches(ignoreCase, 0, prefix, 0, prefix.length()); } /** * Retrieve servletPath from a given location. * where to search the servletPath. * @return ServletPath string value. */ public static String getServletPathFromLocation(final HttpServletRequest request, final String location) { return location.replace(getPathInfoFromLocation(request, location), ""); } /** * Analyze headers of the request and searches for mangled (by proxy) for "Accept-Encoding" header and its mangled * variations and gzip header value and its mangled variations. * * @return true if this request support gzip encoding. */ @SuppressWarnings("unchecked") public static boolean isGzipSupported(final HttpServletRequest request) { if (request != null) { final Enumeration headerNames = request.getHeaderNames(); if (headerNames != null) { while (headerNames.hasMoreElements()) { final String headerName = headerNames.nextElement(); final Matcher m = PATTERN_ACCEPT_ENCODING.matcher(headerName); if (m.find()) { final String headerValue = request.getHeader(headerName); final Matcher mValue = PATTERN_GZIP.matcher(headerValue); return mValue.find(); } } } } return false; } /** * Transforms a java multi-line string into javascript multi-line string. This technique was found at {@link http * ://stackoverflow.com/questions/805107/multiline-strings-in-javascript/} * * @param data * a string containing new lines. * @return a string which being evaluated on the client-side will be treated as a correct multi-line string. */ public static String toJSMultiLineString(final String data) { final StringBuffer result = new StringBuffer("["); if (data != null) { final String[] lines = data.split("\n"); if (lines.length == 0) { result.append("\"\""); } for (int i = 0; i < lines.length; i++) { final String line = lines[i]; result.append("\""); result.append(line.replace("\\", "\\\\").replace("\"", "\\\"").replaceAll("\\r|\\n", "")); // this is used to force a single line to have at least one new line (otherwise cssLint fails). if (lines.length == 1) { result.append("\\n"); } result.append("\""); if (i < lines.length - 1) { result.append(","); } } } result.append("].join(\"\\n\")"); return result.toString(); } /** * Utility used to verify that requestURI matches provided path */ public static boolean matchesUrl(final HttpServletRequest request, final String path) { final Pattern pattern = Pattern.compile(".*" + path + "[/]?", Pattern.CASE_INSENSITIVE); if (request.getRequestURI() != null) { final Matcher m = pattern.matcher(request.getRequestURI()); return m.matches(); } return false; } /** * A simple way to create a {@link WroModelFactory}. * * @param model * {@link WroModel} instance to be returned by the factory. */ public static WroModelFactory factoryFor(final WroModel model) { return new WroModelFactory() { public WroModel create() { return model; } public void destroy() { } }; } public static ObjectFactory simpleObjectFactory(final T object) { return new ObjectFactory() { public T create() { return object; } }; * Wraps original exception into {@link WroRuntimeException} and throw it. * * @param e * the exception to wrap. * @deprecated use {@link WroRuntimeException#wrap(Exception)} */ @Deprecated public static void wrapWithWroRuntimeException(final Exception e) { LOG.error("Exception occured: " + e.getClass(), e.getCause()); if (e instanceof WroRuntimeException) { throw (WroRuntimeException) e; } throw new WroRuntimeException(e.getMessage(), e); } /** * Load the regular expression stored in in regexp.properties resource file. * * @param key * the key of the regexp to load. * @return regular expression value. */ public static String loadRegexpWithKey(final String key) { try { final InputStream stream = WroUtil.class.getResourceAsStream("regexp.properties"); final Properties props = new RegexpProperties().load(stream); return props.getProperty(key); } catch (final IOException e) { throw new WroRuntimeException("Could not load pattern with key: " + key + " from property file", e); } } /** * @return the implementation version of wro4j. */ public static String getImplementationVersion() { return WroUtil.class.getPackage().getImplementationVersion(); } /** * Copy and close the reader and writer streams. * * @param reader * The source stream. * @param writer * The destination stream. * @throws IOException * If content cannot be copy. */ public static void safeCopy(final Reader reader, final Writer writer) throws IOException { try { IOUtils.copy(reader, writer); } finally { IOUtils.closeQuietly(reader); IOUtils.closeQuietly(writer); } } /** * @return a generated {@link File} with unique name located in temp folder. */ public static File createTempFile() { return createTempFile("temp"); } /** * Creates a temp file which has a certain extension. * @param extension of the created temp file. */ public static File createTempFile(final String extension) { try { final String fileName = String.format("wro4j-%s.%s", UUID.randomUUID().toString(), extension); final File file = new File(FileUtils.getTempDirectory(), fileName); file.createNewFile(); return file; } catch (final IOException e) { throw WroRuntimeException.wrap(e); } ======= /* * Copyright (c) 2008. All rights reserved. */ package ro.isdc.wro.util; import static org.apache.commons.lang3.Validate.notNull; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.io.Writer; import java.util.Enumeration; import java.util.Locale; import java.util.Properties; import java.util.TimeZone; import java.util.UUID; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.servlet.http.HttpServletRequest; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.time.FastDateFormat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ro.isdc.wro.WroRuntimeException; import ro.isdc.wro.model.WroModel; import ro.isdc.wro.model.factory.WroModelFactory; import ro.isdc.wro.model.resource.Resource; import ro.isdc.wro.model.resource.processor.ResourcePostProcessor; import ro.isdc.wro.model.resource.processor.ResourcePreProcessor; /** * Utility class. * * @author Alex Objelean * @created Created on Nov 13, 2008 */ public final class WroUtil { private static final Logger LOG = LoggerFactory.getLogger(WroUtil.class); /** * Empty line pattern. */ public static final Pattern EMTPY_LINE_PATTERN = Pattern.compile(loadRegexpWithKey("emptyLine"), Pattern.MULTILINE); /** * Thread safe date format used to transform milliseconds into date as string to put in response header. The localy is * set explicitly to US to conform to specification. */ private static final FastDateFormat DATE_FORMAT = FastDateFormat.getInstance("E, dd MMM yyyy HH:mm:ss z", TimeZone.getTimeZone("GMT"), Locale.US); /** * Patterns used to search for mangled Accept-Encoding header. */ private static final Pattern PATTERN_ACCEPT_ENCODING = Pattern.compile(loadRegexpWithKey("requestHeader.acceptEncoding")); private static final Pattern PATTERN_GZIP = Pattern.compile(loadRegexpWithKey("requestHeader.gzip")); private static final AtomicInteger threadFactoryNumber = new AtomicInteger(1); public static final InputStream EMPTY_STREAM = new ByteArrayInputStream("".getBytes()); /** * @return {@link ThreadFactory} with daemon threads. */ public static ThreadFactory createDaemonThreadFactory(final String name) { return new ThreadFactory() { private final String prefix = "wro4j-" + name + "-" + threadFactoryNumber.getAndIncrement() + "-thread-"; private final AtomicInteger threadNumber = new AtomicInteger(1); public Thread newThread(final Runnable runnable) { final Thread thread = new Thread(runnable, prefix + threadNumber.getAndIncrement()); thread.setDaemon(true); return thread; } }; } /** * Transforms milliseconds into date format for response header of this form: Sat, 10 Apr 2010 17:31:31 GMT. * * @param milliseconds * to transform * @return string representation of the date. */ public static String toDateAsString(final long milliseconds) { return DATE_FORMAT.format(milliseconds); } /** * Retrieve pathInfo from a given location. * * @param request * @param location * where to search contextPath. * @return pathInfo value. */ public static String getPathInfoFromLocation(final HttpServletRequest request, final String location) { if (StringUtils.isEmpty(location)) { throw new IllegalArgumentException("Location cannot be empty string!"); } final String contextPath = request.getContextPath(); if (contextPath != null) { if (startsWithIgnoreCase(location, contextPath)) { return location.substring(contextPath.length()); } else { return location; } } final String noSlash = location.substring(1); final int nextSlash = noSlash.indexOf('/'); if (nextSlash == -1) { return ""; } final String pathInfo = noSlash.substring(nextSlash); * @param str * Case insensitive check if a String starts with a specified prefix. *

*

* nulls are handled without exceptions. Two null references are considered to be equal. The * comparison is case insensitive. *

* *
   * StringUtils.startsWithIgnoreCase(null, null)      = true
   * StringUtils.startsWithIgnoreCase(null, "abcdef")  = false
   * StringUtils.startsWithIgnoreCase("abc", null)     = false
   * StringUtils.startsWithIgnoreCase("abc", "abcdef") = true
   * StringUtils.startsWithIgnoreCase("abc", "ABCDEF") = true
   * 
* * @see java.lang.String#startsWith(String) * @param str * the String to check, may be null * @param prefix * the prefix to find, may be null * @return true if the String starts with the prefix, case insensitive, or both null * @since 2.4 */ public static boolean startsWithIgnoreCase(final String str, final String prefix) { return startsWith(str, prefix, true); } /** * Creates a folder like implementation for a class. Ex: com.mycompany.MyClass -> com/mycompany/ * * @param clazz * used as a base location for determining the package path. * @return a string representation of the path where the class resides. */ public static String toPackageAsFolder(final Class clazz) { Validate.notNull(clazz, "Class cannot be null!"); return clazz.getPackage().getName().replace('.', '/'); } /** *

* Check if a String starts with a specified prefix (optionally case insensitive). *

* * @see java.lang.String#startsWith(String) * the String to check, may be null * @param prefix * the prefix to find, may be null * @param ignoreCase * inidicates whether the compare should ignore case (case insensitive) or not. * @return true if the String starts with the prefix or both null */ private static boolean startsWith(final String str, final String prefix, final boolean ignoreCase) { if (str == null || prefix == null) { return (str == null && prefix == null); } if (prefix.length() > str.length()) { return false; } return str.regionMatches(ignoreCase, 0, prefix, 0, prefix.length()); } /** * Retrieve servletPath from a given location. * * @param location * where to search the servletPath. * @return ServletPath string value. */ public static String getServletPathFromLocation(final HttpServletRequest request, final String location) { return location.replace(getPathInfoFromLocation(request, location), ""); } /** * Analyze headers of the request and searches for mangled (by proxy) for "Accept-Encoding" header and its mangled * variations and gzip header value and its mangled variations. * * @return true if this request support gzip encoding. */ @SuppressWarnings("unchecked") public static boolean isGzipSupported(final HttpServletRequest request) { if (request != null) { final Enumeration headerNames = request.getHeaderNames(); if (headerNames != null) { while (headerNames.hasMoreElements()) { final String headerName = headerNames.nextElement(); final Matcher m = PATTERN_ACCEPT_ENCODING.matcher(headerName); if (m.find()) { final String headerValue = request.getHeader(headerName); final Matcher mValue = PATTERN_GZIP.matcher(headerValue); return mValue.find(); } } } return false; } /** * Transforms a java multi-line string into javascript multi-line string. This technique was found at {@link http * ://stackoverflow.com/questions/805107/multiline-strings-in-javascript/} * * @param data * a string containing new lines. * @return a string which being evaluated on the client-side will be treated as a correct multi-line string. */ public static String toJSMultiLineString(final String data) { final StringBuffer result = new StringBuffer("["); if (data != null) { final String[] lines = data.split("\n"); if (lines.length == 0) { result.append("\"\""); } for (int i = 0; i < lines.length; i++) { final String line = lines[i]; result.append("\""); result.append(line.replace("\\", "\\\\").replace("\"", "\\\"").replaceAll("\\r|\\n", "")); // this is used to force a single line to have at least one new line (otherwise cssLint fails). if (lines.length == 1) { result.append("\\n"); } result.append("\""); if (i < lines.length - 1) { result.append(","); } } } result.append("].join(\"\\n\")"); return result.toString(); } /** * Utility used to verify that requestURI matches provided path */ public static boolean matchesUrl(final HttpServletRequest request, final String path) { final Pattern pattern = Pattern.compile(".*" + path + "[/]?", Pattern.CASE_INSENSITIVE); if (request.getRequestURI() != null) { final Matcher m = pattern.matcher(request.getRequestURI()); return m.matches(); } * A factory method for creating a {@link ResourceProcessor} based on provided {@link ResourcePreProcessor}. * * @param preProcessor * {@link ResourcePreProcessor} to use as a {@link ResourceProcessor}. * @return instance of {@link ResourceProcessor}. */ public static ResourcePostProcessor newResourceProcessor(final Resource resource, final ResourcePreProcessor preProcessor) { return new ResourcePostProcessor() { public void process(final Reader reader, final Writer writer) throws IOException { preProcessor.process(resource, reader, writer); } }; } /** * A simple way to create a {@link WroModelFactory}. * * @param model * {@link WroModel} instance to be returned by the factory. */ public static WroModelFactory factoryFor(final WroModel model) { return new WroModelFactory() { public WroModel create() { return model; } public void destroy() { } }; } public static ObjectFactory simpleObjectFactory(final T object) { return new ObjectFactory() { public T create() { return object; } }; } /** * Load the regular expression stored in in regexp.properties resource file. * * @param key * the key of the regexp to load. * @return regular expression value. */ public static String loadRegexpWithKey(final String key) { try { final InputStream stream = WroUtil.class.getResourceAsStream("regexp.properties"); final Properties props = new RegexpProperties().load(stream); return props.getProperty(key); } catch (final IOException e) { throw new WroRuntimeException("Could not load pattern with key: " + key + " from property file", e); * @return the implementation version of wro4j. */ public static String getImplementationVersion() { return WroUtil.class.getPackage().getImplementationVersion(); } /** * Copy and close the reader and writer streams. * * @param reader * The source stream. * @param writer * The destination stream. * @throws IOException * If content cannot be copy. */ public static void safeCopy(final Reader reader, final Writer writer) throws IOException { try { IOUtils.copy(reader, writer); } finally { IOUtils.closeQuietly(reader); IOUtils.closeQuietly(writer); } } /** * @return a generated {@link File} with unique name located in temp folder. */ public static File createTempFile() { return createTempFile("temp"); } /** * @return a folder with unique name.. */ public static File createTempDirectory() { final String fileName = String.format("wro4j-%s", UUID.randomUUID().toString()); final File file = new File(FileUtils.getTempDirectory(), fileName); file.mkdir(); return file; } /** * Creates a temp file which has a certain extension. * @param extension of the created temp file. */ public static File createTempFile(final String extension) { try { final String fileName = String.format("wro4j-%s.%s", UUID.randomUUID().toString(), extension); final File file = new File(createTempDirectory(), fileName); file.createNewFile(); return file; } catch (final IOException e) { throw WroRuntimeException.wrap(e); } >>>>>>> b552441e99002d2ffd91ba6e5cc91e498e134570 } /**
Solution content
      return "";
    }
/*
 * Copyright (c) 2008. All rights reserved.
 */
package ro.isdc.wro.util;

import static org.apache.commons.lang3.Validate.notNull;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.Writer;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Properties;
import java.util.TimeZone;
import java.util.UUID;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.time.FastDateFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ro.isdc.wro.WroRuntimeException;
import ro.isdc.wro.model.WroModel;
import ro.isdc.wro.model.factory.WroModelFactory;


/**
 * Utility class.
 *
 * @author Alex Objelean
 * @created Created on Nov 13, 2008
 */
public final class WroUtil {
  private static final Logger LOG = LoggerFactory.getLogger(WroUtil.class);
  /**
   * Empty line pattern.
   */
  public static final Pattern EMTPY_LINE_PATTERN = Pattern.compile(loadRegexpWithKey("emptyLine"), Pattern.MULTILINE);
  /**
   * Thread safe date format used to transform milliseconds into date as string to put in response header. The localy is
   * set explicitly to US to conform to specification.
   */
  private static final FastDateFormat DATE_FORMAT = FastDateFormat.getInstance("E, dd MMM yyyy HH:mm:ss z",
      TimeZone.getTimeZone("GMT"), Locale.US);
  /**
   * Patterns used to search for mangled Accept-Encoding header.
   */
  private static final Pattern PATTERN_ACCEPT_ENCODING = Pattern.compile(loadRegexpWithKey("requestHeader.acceptEncoding"));
  private static final Pattern PATTERN_GZIP = Pattern.compile(loadRegexpWithKey("requestHeader.gzip"));

  private static final AtomicInteger threadFactoryNumber = new AtomicInteger(1);
  public static final InputStream EMPTY_STREAM = new ByteArrayInputStream("".getBytes());

  /**
   * @return {@link ThreadFactory} with daemon threads.
   */
  public static ThreadFactory createDaemonThreadFactory(final String name) {
    return new ThreadFactory() {
      private final String prefix = "wro4j-" + name + "-" + threadFactoryNumber.getAndIncrement() + "-thread-";
      private final AtomicInteger threadNumber = new AtomicInteger(1);

      public Thread newThread(final Runnable runnable) {
        final Thread thread = new Thread(runnable, prefix + threadNumber.getAndIncrement());
        thread.setDaemon(true);
        return thread;
      }
    };
  }

  /**
   * Transforms milliseconds into date format for response header of this form: Sat, 10 Apr 2010 17:31:31 GMT.
   *
   * @param milliseconds
   *          to transform
   * @return string representation of the date.
   */
  public static String toDateAsString(final long milliseconds) {
    return DATE_FORMAT.format(milliseconds);
  }

  /**
   * Retrieve pathInfo from a given location.
   *
   * @param request
   * @param location
   *          where to search contextPath.
   * @return pathInfo value.
   */
  public static String getPathInfoFromLocation(final HttpServletRequest request, final String location) {
    if (StringUtils.isEmpty(location)) {
      throw new IllegalArgumentException("Location cannot be empty string!");
    }
    final String contextPath = request.getContextPath();
    if (contextPath != null) {
      if (startsWithIgnoreCase(location, contextPath)) {
        return location.substring(contextPath.length());
      } else {
        return location;
      }
    }
    final String noSlash = location.substring(1);
    final int nextSlash = noSlash.indexOf('/');
    if (nextSlash == -1) {
    final String pathInfo = noSlash.substring(nextSlash);
    return pathInfo;
  }

  /**
   * 

* Case insensitive check if a String starts with a specified prefix. *

*

* nulls are handled without exceptions. Two null references are considered to be equal. The * comparison is case insensitive. *

* *
   * StringUtils.startsWithIgnoreCase(null, null)      = true
   * StringUtils.startsWithIgnoreCase(null, "abcdef")  = false
   * StringUtils.startsWithIgnoreCase("abc", null)     = false
   * StringUtils.startsWithIgnoreCase("abc", "abcdef") = true
   * StringUtils.startsWithIgnoreCase("abc", "ABCDEF") = true
   * 
* * @see java.lang.String#startsWith(String) * @param str * the String to check, may be null * @param prefix * the prefix to find, may be null * @return true if the String starts with the prefix, case insensitive, or both null */ public static boolean startsWithIgnoreCase(final String str, final String prefix) { return startsWith(str, prefix, true); } /** * Creates a folder like implementation for a class. Ex: com.mycompany.MyClass -> com/mycompany/ * * @param clazz * used as a base location for determining the package path. * @return a string representation of the path where the class resides. */ public static String toPackageAsFolder(final Class clazz) { Validate.notNull(clazz, "Class cannot be null!"); return clazz.getPackage().getName().replace('.', '/'); } /** *

* Check if a String starts with a specified prefix (optionally case insensitive). *

* * @see java.lang.String#startsWith(String) * @param str * the String to check, may be null * @param prefix * the prefix to find, may be null * @param ignoreCase * inidicates whether the compare should ignore case (case insensitive) or not. * @return true if the String starts with the prefix or both null */ private static boolean startsWith(final String str, final String prefix, final boolean ignoreCase) { if (str == null || prefix == null) { return (str == null && prefix == null); } if (prefix.length() > str.length()) { return false; } return str.regionMatches(ignoreCase, 0, prefix, 0, prefix.length()); } /** * Retrieve servletPath from a given location. * * @param location * where to search the servletPath. * @return ServletPath string value. */ public static String getServletPathFromLocation(final HttpServletRequest request, final String location) { return location.replace(getPathInfoFromLocation(request, location), ""); } /** * Analyze headers of the request and searches for mangled (by proxy) for "Accept-Encoding" header and its mangled * variations and gzip header value and its mangled variations. * * @return true if this request support gzip encoding. */ @SuppressWarnings("unchecked") public static boolean isGzipSupported(final HttpServletRequest request) { if (request != null) { final Enumeration headerNames = request.getHeaderNames(); if (headerNames != null) { while (headerNames.hasMoreElements()) { final String headerName = headerNames.nextElement(); final Matcher m = PATTERN_ACCEPT_ENCODING.matcher(headerName); if (m.find()) { final String headerValue = request.getHeader(headerName); final Matcher mValue = PATTERN_GZIP.matcher(headerValue); return mValue.find(); } } } } return false; } /** * Transforms a java multi-line string into javascript multi-line string. This technique was found at {@link http * ://stackoverflow.com/questions/805107/multiline-strings-in-javascript/} * * @param data * a string containing new lines. * @return a string which being evaluated on the client-side will be treated as a correct multi-line string. */ public static String toJSMultiLineString(final String data) { final StringBuffer result = new StringBuffer("["); if (data != null) { final String[] lines = data.split("\n"); if (lines.length == 0) { result.append("\"\""); } for (int i = 0; i < lines.length; i++) { final String line = lines[i]; result.append("\""); result.append(line.replace("\\", "\\\\").replace("\"", "\\\"").replaceAll("\\r|\\n", "")); // this is used to force a single line to have at least one new line (otherwise cssLint fails). if (lines.length == 1) { result.append("\\n"); } result.append("\""); if (i < lines.length - 1) { result.append(","); } } } result.append("].join(\"\\n\")"); return result.toString(); } /** * Utility used to verify that requestURI matches provided path */ public static boolean matchesUrl(final HttpServletRequest request, final String path) { final Pattern pattern = Pattern.compile(".*" + path + "[/]?", Pattern.CASE_INSENSITIVE); if (request.getRequestURI() != null) { final Matcher m = pattern.matcher(request.getRequestURI()); return m.matches(); } return false; } /** * A simple way to create a {@link WroModelFactory}. * * @param model * {@link WroModel} instance to be returned by the factory. */ public static WroModelFactory factoryFor(final WroModel model) { return new WroModelFactory() { public WroModel create() { return model; } public void destroy() { } }; } public static ObjectFactory simpleObjectFactory(final T object) { return new ObjectFactory() { public T create() { return object; } }; } /** * Wraps original exception into {@link WroRuntimeException} and throw it. * * @param e * the exception to wrap. * @deprecated use {@link WroRuntimeException#wrap(Exception)} */ @Deprecated public static void wrapWithWroRuntimeException(final Exception e) { LOG.error("Exception occured: " + e.getClass(), e.getCause()); if (e instanceof WroRuntimeException) { throw (WroRuntimeException) e; } throw new WroRuntimeException(e.getMessage(), e); } /** * Load the regular expression stored in in regexp.properties resource file. * * @param key * the key of the regexp to load. * @return regular expression value. */ public static String loadRegexpWithKey(final String key) { try { final InputStream stream = WroUtil.class.getResourceAsStream("regexp.properties"); final Properties props = new RegexpProperties().load(stream); return props.getProperty(key); } catch (final IOException e) { throw new WroRuntimeException("Could not load pattern with key: " + key + " from property file", e); } } /** * @return the implementation version of wro4j. */ public static String getImplementationVersion() { return WroUtil.class.getPackage().getImplementationVersion(); } /** * Copy and close the reader and writer streams. * * @param reader * The source stream. * @param writer * The destination stream. * @throws IOException * If content cannot be copy. */ public static void safeCopy(final Reader reader, final Writer writer) throws IOException { try { IOUtils.copy(reader, writer); } finally { IOUtils.closeQuietly(reader); IOUtils.closeQuietly(writer); } } /** * @return a generated {@link File} with unique name located in temp folder. */ public static File createTempFile() { return createTempFile("temp"); } /** * @return a folder with unique name.. */ public static File createTempDirectory() { final String fileName = String.format("wro4j-%s", UUID.randomUUID().toString()); final File file = new File(FileUtils.getTempDirectory(), fileName); file.mkdir(); return file; } /** * Creates a temp file which has a certain extension. * @param extension of the created temp file. */ public static File createTempFile(final String extension) { try { final String fileName = String.format("wro4j-%s.%s", UUID.randomUUID().toString(), extension); final File file = new File(createTempDirectory(), fileName); file.createNewFile(); return file; } catch (final IOException e) { throw WroRuntimeException.wrap(e); } } /** * Cleans the image url by trimming result and removing \' or \" characters if such exists. * * @param imageUrl * to clean. * @return cleaned image URL. */ public static final String cleanImageUrl(final String imageUrl) { notNull(imageUrl); return imageUrl.replace('\'', ' ').replace('\"', ' ').trim(); } }
File
WroUtil.java
Developer's decision
Manual
Kind of conflict
Annotation
Attribute
Class signature
Comment
Import
Method declaration
Method invocation
Method signature
Package declaration
Try statement
Chunk
Conflicting content
   */
  private boolean incrementalBuildEnabled;
  private TaskExecutor taskExecutor;
<<<<<<< HEAD

=======
>>>>>>> b552441e99002d2ffd91ba6e5cc91e498e134570

  /**
   * {@inheritDoc}
Solution content
   */
  private boolean incrementalBuildEnabled;
  private TaskExecutor taskExecutor;

  /**
   * {@inheritDoc}
File
AbstractWro4jMojo.java
Developer's decision
Version 1
Kind of conflict
Blank
Chunk
Conflicting content
  @Test

<<<<<<< HEAD
/**
 * Copyright Alex Objelean
 */
package ro.isdc.wro.maven.plugin;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
  }


  /**
import java.io.PrintStream;
import java.io.Reader;
import java.io.Writer;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Collection;
import java.util.Date;
import java.util.Properties;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicBoolean;

import org.apache.commons.io.FileUtils;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.MavenProject;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonatype.plexus.build.incremental.BuildContext;

import ro.isdc.wro.config.Context;
import ro.isdc.wro.manager.WroManager.Builder;
import ro.isdc.wro.manager.factory.WroManagerFactory;
import ro.isdc.wro.manager.factory.WroManagerFactoryDecorator;
import ro.isdc.wro.manager.factory.standalone.DefaultStandaloneContextAwareManagerFactory;
import ro.isdc.wro.maven.plugin.manager.factory.ConfigurableWroManagerFactory;
import ro.isdc.wro.model.WroModel;
import ro.isdc.wro.model.group.Group;
import ro.isdc.wro.model.resource.Resource;
import ro.isdc.wro.model.resource.locator.ResourceLocator;
import ro.isdc.wro.model.resource.locator.factory.ResourceLocatorFactory;
import ro.isdc.wro.model.resource.processor.ResourceProcessor;
    victim.execute();
import ro.isdc.wro.model.resource.processor.factory.ConfigurableProcessorsFactory;
import ro.isdc.wro.model.resource.processor.factory.ProcessorsFactory;
import ro.isdc.wro.model.resource.processor.factory.SimpleProcessorsFactory;
import ro.isdc.wro.model.resource.processor.impl.css.CssUrlRewritingProcessor;
import ro.isdc.wro.model.resource.support.hash.HashStrategy;
import ro.isdc.wro.model.resource.support.naming.ConfigurableNamingStrategy;
import ro.isdc.wro.model.resource.support.naming.DefaultHashEncoderNamingStrategy;
import ro.isdc.wro.model.resource.support.naming.FolderHashEncoderNamingStrategy;
import ro.isdc.wro.model.resource.support.naming.NamingStrategy;
import ro.isdc.wro.util.WroTestUtils;
import ro.isdc.wro.util.WroUtil;
import ro.isdc.wro.util.concurrent.TaskExecutor;


/**
 * Test class for {@link Wro4jMojo}
 *
 * @author Alex Objelean
 */
public class TestWro4jMojo {
  private static final Logger LOG = LoggerFactory.getLogger(TestWro4jMojo.class);
  @Mock
  private BuildContext mockBuildContext;
  @Mock
  private HashStrategy mockHashStrategy;
  @Mock
  private ResourceLocatorFactory mockLocatorFactory;
  @Mock
  private ResourceLocator mockLocator;
  private File cssDestinationFolder;
  private File jsDestinationFolder;
  private File destinationFolder;
  private File extraConfigFile;
  private Wro4jMojo victim;

  @Before
  public void setUp()
      throws Exception {
    MockitoAnnotations.initMocks(this);
    when(mockLocator.getInputStream()).thenAnswer(answerWithContent(""));
    Context.set(Context.standaloneContext());
    victim = new Wro4jMojo();
    setUpMojo(victim);
  }

  /**
   * Perform basic initialization with valid values of the provided mojo.
   */
  private void setUpMojo(final Wro4jMojo mojo)
      throws Exception {
    mojo.setIgnoreMissingResources(false);
    mojo.setMinimize(true);
    setWroWithValidResources();
    destinationFolder = new File(FileUtils.getTempDirectory(), "wroTemp-" + new Date().getTime());
    destinationFolder.mkdir();
    cssDestinationFolder = new File(FileUtils.getTempDirectory(), "wroTemp-css-" + new Date().getTime());
    destinationFolder.mkdir();
    jsDestinationFolder = new File(FileUtils.getTempDirectory(), "wroTemp-js-" + new Date().getTime());
    destinationFolder.mkdir();
    extraConfigFile = new File(FileUtils.getTempDirectory(), "extraConfig-" + new Date().getTime());
    extraConfigFile.createNewFile();
    mojo.setBuildDirectory(destinationFolder);
    mojo.setExtraConfigFile(extraConfigFile);
    mojo.setDestinationFolder(destinationFolder);
    mojo.setMavenProject(Mockito.mock(MavenProject.class));
    mojo.setBuildContext(mockBuildContext);
  }

  private void setWroFile(final String classpathResourceName)
      throws URISyntaxException {
    final URL url = getClass().getClassLoader().getResource(classpathResourceName);
    final File wroFile = new File(url.toURI());
    victim.setWroFile(wroFile);
    victim.setContextFolder(wroFile.getParentFile().getParentFile());
  }

  private void setWroWithValidResources()
      throws Exception {
    setWroFile("wro.xml");
  }

  private void setWroWithInvalidResources()
      throws Exception {
    setWroFile("wroWithInvalidResources.xml");
  }

  @Test
  public void testMojoWithPropertiesSetAndOneTargetGroup()
      throws Exception {
    victim.setTargetGroups("g1");
    victim.setIgnoreMissingResources(true);
    victim.execute();
  }

  @Test(expected = MojoExecutionException.class)
  public void shouldFailWhenInvalidResourcesAreUsed()
      throws Exception {
    victim.setIgnoreMissingResources(false);
    victim.execute();
  }

  @Test(expected = MojoExecutionException.class)
  public void testNoDestinationFolderSet()
      throws Exception {
    victim.setDestinationFolder(null);
    victim.execute();
  }

  @Test(expected = MojoExecutionException.class)
  public void testOnlyCssDestinationFolderSet()
      throws Exception {
    victim.setCssDestinationFolder(cssDestinationFolder);
    victim.setDestinationFolder(null);
    victim.execute();
  }

  @Test(expected = MojoExecutionException.class)
  public void testOnlyJsDestinationFolderSet()
      throws Exception {
    victim.setJsDestinationFolder(jsDestinationFolder);
    victim.setDestinationFolder(null);
    victim.execute();
  }

  @Test
  public void testJsAndCssDestinationFolderSet()
      throws Exception {
    victim.setIgnoreMissingResources(true);
    victim.setJsDestinationFolder(jsDestinationFolder);
    victim.setCssDestinationFolder(cssDestinationFolder);
    victim.execute();
  }

  @Test(expected = MojoExecutionException.class)
  public void cannotExecuteWhenInvalidResourcesPresentAndDoNotIgnoreMissingResources()
      throws Exception {
    setWroWithInvalidResources();
    victim.setIgnoreMissingResources(false);
    victim.execute();
  }

  @Test
  public void testWroXmlWithInvalidResourcesAndIgnoreMissingResourcesTrue()
      throws Exception {
    setWroWithInvalidResources();
    victim.setIgnoreMissingResources(true);
    victim.execute();
  }

  @Test(expected = MojoExecutionException.class)
  public void testMojoWithWroManagerFactorySet()
      throws Exception {
    victim.setWroManagerFactory(ExceptionThrowingWroManagerFactory.class.getName());
    victim.execute();
  }

  @Test(expected = MojoExecutionException.class)

  public void testInvalidMojoWithWroManagerFactorySet()
      throws Exception {
    victim.setWroManagerFactory("INVALID_CLASS_NAME");
    victim.execute();
  }

  @Test
  public void executeWithNullTargetGroupsProperty()
      throws Exception {
    victim.setIgnoreMissingResources(true);
    victim.setTargetGroups(null);
    victim.execute();
  }

  @Test(expected = MojoExecutionException.class)
  public void testMojoWithCustomManagerFactoryWithInvalidResourceAndNotIgnoreMissingResources()
      throws Exception {
    setWroWithInvalidResources();
    victim.setIgnoreMissingResources(false);
    victim.setWroManagerFactory(CustomManagerFactory.class.getName());
    victim.execute();
  }

  @Test
  public void testMojoWithCustomManagerFactoryWithInvalidResourceAndIgnoreMissingResources()
      throws Exception {
    setWroWithInvalidResources();
    victim.setIgnoreMissingResources(true);
    victim.setWroManagerFactory(CustomManagerFactory.class.getName());
    victim.execute();
  }

  @Test(expected = MojoExecutionException.class)
  public void testMojoWithConfigurableWroManagerFactory()
      throws Exception {
    setWroWithValidResources();
    victim.setIgnoreMissingResources(true);
    // by default a valid file is used, set null explicitly
    victim.setExtraConfigFile(null);
    // verify
    victim.setWroManagerFactory(ConfigurableWroManagerFactory.class.getName());
    victim.execute();
  }

  @Test
  public void testMojoWithConfigurableWroManagerFactoryWithValidAndEmptyConfigFileSet()
      throws Exception {
    setWroWithValidResources();
    victim.setIgnoreMissingResources(true);
    victim.setWroManagerFactory(ConfigurableWroManagerFactory.class.getName());
    victim.execute();
  }

  @Test
  public void testMojoWithConfigurableWroManagerFactoryWithValidConfigFileSet()
      throws Exception {
    setWroWithValidResources();
    final String preProcessors = ConfigurableProcessorsFactory.PARAM_PRE_PROCESSORS + "=cssMin";
    FileUtils.write(extraConfigFile, preProcessors);

    victim.setIgnoreMissingResources(true);
    victim.setWroManagerFactory(ConfigurableWroManagerFactory.class.getName());
    victim.execute();
  }

  @Test
  public void shouldBeFasterWhenRunningProcessingInParallel() throws Exception {
    final long begin = System.currentTimeMillis();
    victim.setParallelProcessing(false);
    testMojoWithConfigurableWroManagerFactoryWithValidConfigFileSet();
    final long endSerial = System.currentTimeMillis();
    victim.setParallelProcessing(true);
    testMojoWithConfigurableWroManagerFactoryWithValidConfigFileSet();
    final long endParallel = System.currentTimeMillis();

    final long serial = endSerial - begin;
    final long parallel = endParallel - endSerial;
    LOG.info("serial took: {}ms", serial);
    LOG.info("parallel took: {}ms", parallel);
    assertTrue(serial > parallel);
  }

  @Test
  public void shouldUseTaskExecutorWhenRunningInParallel() throws Exception {
    final AtomicBoolean invoked = new AtomicBoolean();
    final TaskExecutor taskExecutor = new TaskExecutor() {
      @Override
      public void submit(final Collection> callables)
          throws Exception {
        invoked.set(true);
        super.submit(callables);
      }
    };
    victim.setTaskExecutor(taskExecutor);
    victim.setIgnoreMissingResources(true);

    victim.setParallelProcessing(false);
    victim.execute();
    assertFalse(invoked.get());

    victim.setParallelProcessing(true);
    victim.execute();
    assertTrue(invoked.get());
  }

  @Test
  public void testComputedAggregatedFolder()
      throws Exception {
    setWroWithValidResources();
    victim.setWroManagerFactory(CssUrlRewriterWroManagerFactory.class.getName());
    victim.setIgnoreMissingResources(true);
    final File cssDestinationFolder = new File(this.destinationFolder, "subfolder");
    cssDestinationFolder.mkdir();
    victim.setCssDestinationFolder(cssDestinationFolder);
    victim.execute();
  }

  @Test(expected = MojoExecutionException.class)
  public void testMojoWithConfigurableWroManagerFactoryWithInvalidPreProcessor()
      throws Exception {
    setWroWithValidResources();
    final String preProcessors = ConfigurableProcessorsFactory.PARAM_PRE_PROCESSORS + "=INVALID";
    FileUtils.write(extraConfigFile, preProcessors);

    victim.setIgnoreMissingResources(true);
    victim.setWroManagerFactory(ConfigurableWroManagerFactory.class.getName());
    victim.execute();
  }

  @Test
  public void shouldGenerateGroupMappingUsingNoOpNamingStrategy()
      throws Exception {
    setWroWithValidResources();

    final File groupNameMappingFile = new File(FileUtils.getTempDirectory(), "groupMapping-" + new Date().getTime());

    victim.setGroupNameMappingFile(groupNameMappingFile);
    victim.setIgnoreMissingResources(true);
    final Properties groupNames = new Properties();
    groupNames.load(new FileInputStream(groupNameMappingFile));
    LOG.debug("groupNames: {}", groupNames);
    assertEquals("g1.js", groupNames.get("g1.js"));

    FileUtils.deleteQuietly(groupNameMappingFile);
  }

  @Test
  public void shouldGenerateGroupMappingUsingCustomNamingStrategy()
      throws Exception {
    setWroWithValidResources();

    final File groupNameMappingFile = new File(FileUtils.getTempDirectory(), "groupMapping-" + new Date().getTime());

    victim.setWroManagerFactory(CustomNamingStrategyWroManagerFactory.class.getName());
    victim.setGroupNameMappingFile(groupNameMappingFile);
    victim.setIgnoreMissingResources(true);
    victim.execute();

    // verify
    final Properties groupNames = new Properties();
    groupNames.load(new FileInputStream(groupNameMappingFile));
    LOG.debug("groupNames: {}", groupNames);
    Assert.assertEquals(CustomNamingStrategyWroManagerFactory.PREFIX + "g1.js", groupNames.get("g1.js"));

    FileUtils.deleteQuietly(groupNameMappingFile);
  }

  @Test
  public void shouldSkipSecondProcessingWhenIncrementalBuildEnabled() throws Exception {
    victim.setBuildContext(null);
    victim.setIncrementalBuildEnabled(true);
    testMojoWithConfigurableWroManagerFactoryWithValidConfigFileSet();
    testMojoWithConfigurableWroManagerFactoryWithValidConfigFileSet();
  }

  @Test
  public void shouldUseConfiguredNamingStrategy()
      throws Exception {
    setWroWithValidResources();

    final File extraConfigFile = new File(FileUtils.getTempDirectory(), "groupMapping-" + new Date().getTime());

    final Properties props = new Properties();
    // TODO create a properties builder
    props.setProperty(ConfigurableNamingStrategy.KEY, FolderHashEncoderNamingStrategy.ALIAS);
    props.list(new PrintStream(extraConfigFile));

    victim.setWroManagerFactory(ConfigurableWroManagerFactory.class.getName());
    victim.setExtraConfigFile(extraConfigFile);
    victim.setIgnoreMissingResources(true);
    victim.execute();

    FileUtils.deleteQuietly(extraConfigFile);
      final SimpleProcessorsFactory factory = new SimpleProcessorsFactory();
import ro.isdc.wro.config.Context;
  public static final class ExceptionThrowingWroManagerFactory
      extends DefaultStandaloneContextAwareManagerFactory {
    @Override
    protected ProcessorsFactory newProcessorsFactory() {
      final SimpleProcessorsFactory factory = new SimpleProcessorsFactory();
      final ResourceProcessor postProcessor = Mockito.mock(ResourceProcessor.class);
      try {
        Mockito.doThrow(new RuntimeException()).when(postProcessor).process(Mockito.any(Resource.class),
            Mockito.any(Reader.class), Mockito.any(Writer.class));
      } catch (final IOException e) {
        Assert.fail("never happen");
      }
      factory.addPostProcessor(postProcessor);
      return factory;
    }
  }

  public static class CustomManagerFactory
      extends DefaultStandaloneContextAwareManagerFactory {
  }

  public static final class CustomNamingStrategyWroManagerFactory
      extends DefaultStandaloneContextAwareManagerFactory {
    public static final String PREFIX = "renamed";
    {
      setNamingStrategy(new NamingStrategy() {
        public String rename(final String originalName, final InputStream inputStream)
            throws IOException {
          return PREFIX + originalName;
        }
      });
    }
  }

  public static final class CssUrlRewriterWroManagerFactory
      extends DefaultStandaloneContextAwareManagerFactory {
    @Override
    protected ProcessorsFactory newProcessorsFactory() {
      factory.addPreProcessor(new CssUrlRewritingProcessor());
      return factory;
    }
  }

  @Test
  public void shouldDetectIncrementalChange()
      throws Exception {
    victim = new Wro4jMojo() {
      @Override
      protected WroManagerFactory getManagerFactory() {
        return new WroManagerFactoryDecorator(super.getManagerFactory()) {
          @Override
          protected void onBeforeBuild(final Builder builder) {
            builder.setHashStrategy(mockHashStrategy);
          }
        };
      }
    };
    setUpMojo(victim);
    final String hashValue = "SomeHashValue";
    when(mockHashStrategy.getHash(Mockito.any(InputStream.class))).thenReturn(hashValue);
    when(mockBuildContext.isIncremental()).thenReturn(true);
    when(mockBuildContext.getValue(Mockito.anyString())).thenReturn(hashValue);
    victim.setIgnoreMissingResources(true);
    // incremental build detects no change
    assertTrue(victim.getTargetGroupsAsList().isEmpty());

    // incremental change detects change for all resources
    when(mockHashStrategy.getHash(Mockito.any(InputStream.class))).thenReturn("TotallyDifferentValue");
    assertFalse(victim.getTargetGroupsAsList().isEmpty());
  }

  @Test
  public void shouldDetectIncrementalChangeOfImportedCss()
      throws Exception {
    final String importResource = "imported.css";

    configureMojoForModelWithImportedCssResource(importResource);
    // incremental build detects no change
    assertTrue(victim.getTargetGroupsAsList().isEmpty());

    when(mockLocatorFactory.locate(Mockito.eq(importResource))).thenAnswer(answerWithContent("Changed"));
    assertFalse(victim.getTargetGroupsAsList().isEmpty());
  }

  private void configureMojoForModelWithImportedCssResource(final String importResource) throws Exception {
    final String parentResource = "parent.css";

    final WroModel model = new WroModel();
    model.addGroup(new Group("g1").addResource(Resource.create(parentResource)));
    when(mockLocatorFactory.locate(Mockito.anyString())).thenAnswer(answerWithContent(""));
    final String parentContent = String.format("@import url(%s)", importResource);
import ro.isdc.wro.manager.WroManager.Builder;

import ro.isdc.wro.manager.factory.WroManagerFactory;
    when(mockLocatorFactory.locate(Mockito.eq(parentResource))).thenAnswer(answerWithContent(parentContent));

    victim = new Wro4jMojo() {
      @Override
      protected WroManagerFactory newWroManagerFactory()
          throws MojoExecutionException {
        final DefaultStandaloneContextAwareManagerFactory managerFactory = new DefaultStandaloneContextAwareManagerFactory();
        managerFactory.setLocatorFactory(mockLocatorFactory);
        managerFactory.setModelFactory(WroTestUtils.simpleModelFactory(model));
        return managerFactory;
      }
    };
    final HashStrategy hashStrategy = victim.getManagerFactory().create().getHashStrategy();
    setUpMojo(victim);

    final String importedInitialContent = "initial";

    when(mockLocatorFactory.locate(Mockito.eq(importResource))).thenAnswer(answerWithContent(importedInitialContent));
    when(mockBuildContext.isIncremental()).thenReturn(true);
    when(mockBuildContext.getValue(Mockito.eq(parentResource))).thenReturn(
        hashStrategy.getHash(new ByteArrayInputStream(parentContent.getBytes())));
    when(mockBuildContext.getValue(Mockito.eq(importResource))).thenReturn(
        hashStrategy.getHash(new ByteArrayInputStream(importedInitialContent.getBytes())));
    victim.setIgnoreMissingResources(true);
  }

  @Test
  public void shouldIgnoreChangesOfGroupsWhichAreNotPartOfTargetGroups() throws Exception {
    final String importResource = "imported.css";

    configureMojoForModelWithImportedCssResource(importResource);
    victim.setTargetGroups("g2");

    // incremental build detects no change
    assertTrue(victim.getTargetGroupsAsList().isEmpty());
    when(mockLocatorFactory.locate(Mockito.eq(importResource))).thenAnswer(answerWithContent("Changed"));
    assertTrue(victim.getTargetGroupsAsList().isEmpty());
  }

  @Test
  public void shouldReuseGroupNameMappingFileWithIncrementalBuild()
      throws Exception {
    final File groupNameMappingFile = WroUtil.createTempFile();

    final Resource g1Resource = spy(Resource.create("1.js"));
    try {
      final WroModel model = new WroModel();
      model.addGroup(new Group("g1").addResource(g1Resource));
      model.addGroup(new Group("g2").addResource(Resource.create("2.js")));
      victim = new Wro4jMojo() {
        @Override
        protected WroManagerFactory newWroManagerFactory()
            throws MojoExecutionException {
          final DefaultStandaloneContextAwareManagerFactory managerFactory = new DefaultStandaloneContextAwareManagerFactory();
          managerFactory.setLocatorFactory(WroTestUtils.createResourceMockingLocatorFactory());
          managerFactory.setModelFactory(WroTestUtils.simpleModelFactory(model));
          managerFactory.setNamingStrategy(new DefaultHashEncoderNamingStrategy());
          return managerFactory;
        }
      };
      setUpMojo(victim);

      victim.setGroupNameMappingFile(groupNameMappingFile);

      assertEquals(2, victim.getTargetGroupsAsList().size());
      victim.execute();

      //Now mark it as incremental
      when(mockBuildContext.isIncremental()).thenReturn(true);

      final Properties groupNames = new Properties();
      groupNames.load(new FileInputStream(groupNameMappingFile));
      assertEquals(4, groupNames.entrySet().size());

      //change the uri of the resource from group a (equivalent to changing its content).
      when(g1Resource.getUri()).thenReturn("1a.js");

      assertEquals(1, victim.getTargetGroupsAsList().size());
      victim.execute();

      groupNames.load(new FileInputStream(groupNameMappingFile));
      // The number of persisted groupNames should still be unchanged, even though only a single group has been changed
      // after incremental build.
      assertEquals(4, groupNames.entrySet().size());
    } finally {
      FileUtils.deleteQuietly(groupNameMappingFile);
    }
  }
    assertEquals(9, victim.getTargetGroupsAsList().size());

    victim.doExecute();
  }

   * Test for the following scenario: when incremental build is performed, only changed resources are processed. Given
   * that there are two target groups, and a resource from only one group is changed incremental build should process
   * only that one group. However, if the targetFolder does not exist, all target groups must be processed.
   */
  @Test
  public void shouldProcessTargetGroupsWhenDestinationFolderDoesNotExist() throws Exception {
    victim = new Wro4jMojo() {
      @Override
      protected WroManagerFactory getManagerFactory() {
        return new WroManagerFactoryDecorator(super.getManagerFactory()) {
          @Override
          protected void onBeforeBuild(final Builder builder) {
            builder.setHashStrategy(mockHashStrategy);
          }
        };
      }
    };
    final String constantHash = "hash";
    when(mockHashStrategy.getHash(Mockito.any(InputStream.class))).thenReturn(constantHash);
    setUpMojo(victim);
    victim.setIgnoreMissingResources(true);

    final int totalGroups = 9;

    assertEquals(totalGroups, victim.getTargetGroupsAsList().size());

    when(mockBuildContext.isIncremental()).thenReturn(true);
    when(mockBuildContext.getValue(Mockito.anyString())).thenReturn(constantHash);

    assertEquals(0, victim.getTargetGroupsAsList().size());

    //delete target folder
    destinationFolder.delete();

  private Answer answerWithContent(final String content) {
    return new Answer() {
      public InputStream answer(final InvocationOnMock invocation)
          throws Throwable {
        return new ByteArrayInputStream(content.getBytes());
      }
    };
  }

  @After
  public void tearDown()
      throws Exception {
    victim.clean();
    FileUtils.deleteDirectory(destinationFolder);
    FileUtils.deleteDirectory(cssDestinationFolder);
    FileUtils.deleteDirectory(jsDestinationFolder);
    FileUtils.deleteQuietly(extraConfigFile);
  }
}
=======
/**
 * Copyright Alex Objelean
 */
package ro.isdc.wro.maven.plugin;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.Reader;
import java.io.Writer;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Collection;
import java.util.Date;
import java.util.Properties;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicBoolean;

import org.apache.commons.io.FileUtils;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.MavenProject;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonatype.plexus.build.incremental.BuildContext;

  @Mock
  private HashStrategy mockHashStrategy;
  private void setWroWithValidResources()
      throws Exception {
import ro.isdc.wro.manager.factory.WroManagerFactoryDecorator;
import ro.isdc.wro.manager.factory.standalone.DefaultStandaloneContextAwareManagerFactory;
import ro.isdc.wro.maven.plugin.manager.factory.ConfigurableWroManagerFactory;
import ro.isdc.wro.model.WroModel;
import ro.isdc.wro.model.group.Group;
import ro.isdc.wro.model.resource.Resource;
import ro.isdc.wro.model.resource.locator.UriLocator;
import ro.isdc.wro.model.resource.locator.factory.UriLocatorFactory;
import ro.isdc.wro.model.resource.processor.ResourcePostProcessor;
import ro.isdc.wro.model.resource.processor.factory.ConfigurableProcessorsFactory;
import ro.isdc.wro.model.resource.processor.factory.ProcessorsFactory;
import ro.isdc.wro.model.resource.processor.factory.SimpleProcessorsFactory;
import ro.isdc.wro.model.resource.processor.impl.css.CssUrlRewritingProcessor;
import ro.isdc.wro.model.resource.support.hash.HashStrategy;
import ro.isdc.wro.model.resource.support.naming.ConfigurableNamingStrategy;
import ro.isdc.wro.model.resource.support.naming.DefaultHashEncoderNamingStrategy;
import ro.isdc.wro.model.resource.support.naming.FolderHashEncoderNamingStrategy;
import ro.isdc.wro.model.resource.support.naming.NamingStrategy;
import ro.isdc.wro.util.WroTestUtils;
import ro.isdc.wro.util.WroUtil;
import ro.isdc.wro.util.concurrent.TaskExecutor;


/**
 * Test class for {@link Wro4jMojo}
 *
 * @author Alex Objelean
 */
public class TestWro4jMojo {
  private static final Logger LOG = LoggerFactory.getLogger(TestWro4jMojo.class);
  @Mock
  private BuildContext mockBuildContext;
  private UriLocatorFactory mockLocatorFactory;
  @Mock
  private UriLocator mockLocator;
  private File cssDestinationFolder;
  private File jsDestinationFolder;
  private File destinationFolder;
  private File extraConfigFile;
  private Wro4jMojo victim;

  @Before
  public void setUp()
      throws Exception {
    MockitoAnnotations.initMocks(this);
    mockLocatorFactory = new UriLocatorFactory() {
      public InputStream locate(final String uri)
          throws IOException {
        return mockLocator.locate(uri);
      }

      public UriLocator getInstance(final String uri) {
        return mockLocator;
      }
    };
    Context.set(Context.standaloneContext());
    victim = new Wro4jMojo();
    setUpMojo(victim);
  }

  /**
   * Perform basic initialization with valid values of the provided mojo.
   */
  private void setUpMojo(final Wro4jMojo mojo)
      throws Exception {
    mojo.setIgnoreMissingResources(false);
    mojo.setMinimize(true);
    setWroWithValidResources();
    destinationFolder = new File(FileUtils.getTempDirectory(), "wroTemp-" + new Date().getTime());
    destinationFolder.mkdir();
    cssDestinationFolder = new File(FileUtils.getTempDirectory(), "wroTemp-css-" + new Date().getTime());
    destinationFolder.mkdir();
    jsDestinationFolder = new File(FileUtils.getTempDirectory(), "wroTemp-js-" + new Date().getTime());
    destinationFolder.mkdir();
    extraConfigFile = new File(FileUtils.getTempDirectory(), "extraConfig-" + new Date().getTime());
    extraConfigFile.createNewFile();
    mojo.setBuildDirectory(destinationFolder);
    mojo.setExtraConfigFile(extraConfigFile);
    mojo.setDestinationFolder(destinationFolder);
    mojo.setMavenProject(Mockito.mock(MavenProject.class));
    mojo.setBuildContext(mockBuildContext);
  }

  private void setWroFile(final String classpathResourceName)
      throws URISyntaxException {
    final URL url = getClass().getClassLoader().getResource(classpathResourceName);
    final File wroFile = new File(url.toURI());
    victim.setWroFile(wroFile);
    victim.setContextFolder(wroFile.getParentFile().getPath());
  }

    setWroWithInvalidResources();
    setWroFile("wro.xml");
  }

  private void setWroWithInvalidResources()
      throws Exception {
    setWroFile("wroWithInvalidResources.xml");
  }

  @Test
  public void testMojoWithPropertiesSetAndOneTargetGroup()
      throws Exception {
    victim.setTargetGroups("g1");
    victim.setIgnoreMissingResources(true);
    victim.execute();
  }

  @Test(expected = MojoExecutionException.class)
  public void shouldFailWhenInvalidResourcesAreUsed()
      throws Exception {
    victim.setIgnoreMissingResources(false);
    victim.execute();
  }

  @Test(expected = MojoExecutionException.class)
  public void testNoDestinationFolderSet()
      throws Exception {
    victim.setDestinationFolder(null);
    victim.execute();
  }

  @Test(expected = MojoExecutionException.class)
  public void testOnlyCssDestinationFolderSet()
      throws Exception {
    victim.setCssDestinationFolder(cssDestinationFolder);
    victim.setDestinationFolder(null);
    victim.execute();
  }

  @Test(expected = MojoExecutionException.class)
  public void testOnlyJsDestinationFolderSet()
      throws Exception {
    victim.setJsDestinationFolder(jsDestinationFolder);
    victim.setDestinationFolder(null);
    victim.execute();
  }

  @Test
  public void testJsAndCssDestinationFolderSet()
      throws Exception {
    victim.setIgnoreMissingResources(true);
    victim.setJsDestinationFolder(jsDestinationFolder);
    victim.setCssDestinationFolder(cssDestinationFolder);
    victim.execute();
  }

  @Test(expected = MojoExecutionException.class)
  public void cannotExecuteWhenInvalidResourcesPresentAndDoNotIgnoreMissingResources()
      throws Exception {
    setWroWithInvalidResources();
    victim.setIgnoreMissingResources(false);
    victim.execute();
  }

  @Test
  public void testWroXmlWithInvalidResourcesAndIgnoreMissingResourcesTrue()
      throws Exception {
    setWroWithInvalidResources();
    victim.setIgnoreMissingResources(true);
    victim.execute();
  }

  @Test(expected = MojoExecutionException.class)
  public void testMojoWithWroManagerFactorySet()
      throws Exception {
    victim.setWroManagerFactory(ExceptionThrowingWroManagerFactory.class.getName());
    victim.execute();
  }

  @Test(expected = MojoExecutionException.class)
  public void testInvalidMojoWithWroManagerFactorySet()
      throws Exception {
    victim.setWroManagerFactory("INVALID_CLASS_NAME");
    victim.execute();
  }

  @Test
  public void executeWithNullTargetGroupsProperty()
      throws Exception {
    victim.setIgnoreMissingResources(true);
    victim.setTargetGroups(null);
    victim.execute();
  }

  @Test(expected = MojoExecutionException.class)
  public void testMojoWithCustomManagerFactoryWithInvalidResourceAndNotIgnoreMissingResources()
      throws Exception {
    setWroWithInvalidResources();
    victim.setIgnoreMissingResources(false);
    victim.setWroManagerFactory(CustomManagerFactory.class.getName());
    victim.execute();
  }

  @Test
  public void testMojoWithCustomManagerFactoryWithInvalidResourceAndIgnoreMissingResources()
      throws Exception {
    victim.setIgnoreMissingResources(true);
    victim.execute();
  }

    victim.setWroManagerFactory(CustomManagerFactory.class.getName());
    victim.execute();
  }

  @Test(expected = MojoExecutionException.class)
  public void testMojoWithConfigurableWroManagerFactory()
      throws Exception {
    setWroWithValidResources();
    victim.setIgnoreMissingResources(true);
    // by default a valid file is used, set null explicitly
    victim.setExtraConfigFile(null);
    victim.setWroManagerFactory(ConfigurableWroManagerFactory.class.getName());
    victim.execute();
  }

  @Test
  public void testMojoWithConfigurableWroManagerFactoryWithValidAndEmptyConfigFileSet()
      throws Exception {
    setWroWithValidResources();
    victim.setIgnoreMissingResources(true);
    victim.setWroManagerFactory(ConfigurableWroManagerFactory.class.getName());
    victim.execute();
  }

  @Test
  public void testMojoWithConfigurableWroManagerFactoryWithValidConfigFileSet()
      throws Exception {
    setWroWithValidResources();
    final String preProcessors = ConfigurableProcessorsFactory.PARAM_PRE_PROCESSORS + "=cssMin";
    FileUtils.write(extraConfigFile, preProcessors);

    victim.setIgnoreMissingResources(true);
    victim.setWroManagerFactory(ConfigurableWroManagerFactory.class.getName());
    victim.execute();
  }

  @Test
  public void shouldBeFasterWhenRunningProcessingInParallel() throws Exception {
    final long begin = System.currentTimeMillis();
    victim.setParallelProcessing(false);
    testMojoWithConfigurableWroManagerFactoryWithValidConfigFileSet();
    final long endSerial = System.currentTimeMillis();
    victim.setParallelProcessing(true);
    testMojoWithConfigurableWroManagerFactoryWithValidConfigFileSet();
    final long endParallel = System.currentTimeMillis();

    final long serial = endSerial - begin;
    final long parallel = endParallel - endSerial;
    LOG.info("serial took: {}ms", serial);
    LOG.info("parallel took: {}ms", parallel);
    assertTrue(serial > parallel);
  }

  @Test
  public void shouldUseTaskExecutorWhenRunningInParallel() throws Exception {
    final AtomicBoolean invoked = new AtomicBoolean();
    final TaskExecutor taskExecutor = new TaskExecutor() {
      @Override
      public void submit(final Collection> callables)
          throws Exception {
        invoked.set(true);
        super.submit(callables);
      }
    };
    victim.setTaskExecutor(taskExecutor);
    victim.setIgnoreMissingResources(true);

    victim.setParallelProcessing(false);
    victim.execute();
    assertFalse(invoked.get());

    victim.setParallelProcessing(true);
    victim.execute();
    assertTrue(invoked.get());
  }

  @Test
  public void testComputedAggregatedFolder()
      throws Exception {
    setWroWithValidResources();
    victim.setWroManagerFactory(CssUrlRewriterWroManagerFactory.class.getName());
    victim.setIgnoreMissingResources(true);
    final File cssDestinationFolder = new File(this.destinationFolder, "subfolder");
    cssDestinationFolder.mkdir();
    victim.setCssDestinationFolder(cssDestinationFolder);
    victim.execute();
  }

  @Test(expected = MojoExecutionException.class)
  public void testMojoWithConfigurableWroManagerFactoryWithInvalidPreProcessor()
      throws Exception {
    setWroWithValidResources();
    final String preProcessors = ConfigurableProcessorsFactory.PARAM_PRE_PROCESSORS + "=INVALID";
    FileUtils.write(extraConfigFile, preProcessors);

    victim.setIgnoreMissingResources(true);
    victim.setWroManagerFactory(ConfigurableWroManagerFactory.class.getName());
            throws IOException {
  public void shouldGenerateGroupMappingUsingNoOpNamingStrategy()
      throws Exception {
    setWroWithValidResources();

    final File groupNameMappingFile = new File(FileUtils.getTempDirectory(), "groupMapping-" + new Date().getTime());

    victim.setGroupNameMappingFile(groupNameMappingFile);
    victim.setIgnoreMissingResources(true);
    victim.execute();

    // verify
    final Properties groupNames = new Properties();
    groupNames.load(new FileInputStream(groupNameMappingFile));
    LOG.debug("groupNames: {}", groupNames);
    assertEquals("g1.js", groupNames.get("g1.js"));

    FileUtils.deleteQuietly(groupNameMappingFile);
  }

  @Test
  public void shouldGenerateGroupMappingUsingCustomNamingStrategy()
      throws Exception {
    setWroWithValidResources();

    final File groupNameMappingFile = new File(FileUtils.getTempDirectory(), "groupMapping-" + new Date().getTime());

    victim.setWroManagerFactory(CustomNamingStrategyWroManagerFactory.class.getName());
    victim.setGroupNameMappingFile(groupNameMappingFile);
    victim.setIgnoreMissingResources(true);
    victim.execute();

    // verify
    final Properties groupNames = new Properties();
    groupNames.load(new FileInputStream(groupNameMappingFile));
    LOG.debug("groupNames: {}", groupNames);
    // incremental build detects no change
    Assert.assertEquals(CustomNamingStrategyWroManagerFactory.PREFIX + "g1.js", groupNames.get("g1.js"));

    FileUtils.deleteQuietly(groupNameMappingFile);
  }

  @Test
  public void shouldSkipSecondProcessingWhenIncrementalBuildEnabled() throws Exception {
    victim.setBuildContext(null);
    victim.setIncrementalBuildEnabled(true);
    testMojoWithConfigurableWroManagerFactoryWithValidConfigFileSet();
    testMojoWithConfigurableWroManagerFactoryWithValidConfigFileSet();
  }

  @Test
  public void shouldUseConfiguredNamingStrategy()
      throws Exception {
    setWroWithValidResources();

    final File extraConfigFile = new File(FileUtils.getTempDirectory(), "groupMapping-" + new Date().getTime());

    final Properties props = new Properties();
    // TODO create a properties builder
    props.setProperty(ConfigurableNamingStrategy.KEY, FolderHashEncoderNamingStrategy.ALIAS);
    props.list(new PrintStream(extraConfigFile));

    victim.setWroManagerFactory(ConfigurableWroManagerFactory.class.getName());
    victim.setExtraConfigFile(extraConfigFile);
    victim.setIgnoreMissingResources(true);
    victim.execute();

    FileUtils.deleteQuietly(extraConfigFile);
  }

  public static final class ExceptionThrowingWroManagerFactory
      extends DefaultStandaloneContextAwareManagerFactory {
    @Override
    protected ProcessorsFactory newProcessorsFactory() {
      final SimpleProcessorsFactory factory = new SimpleProcessorsFactory();
      final ResourcePostProcessor postProcessor = Mockito.mock(ResourcePostProcessor.class);
      try {
        Mockito.doThrow(new RuntimeException()).when(postProcessor).process(Mockito.any(Reader.class),
            Mockito.any(Writer.class));
      } catch (final IOException e) {
        Assert.fail("never happen");
      }
      factory.addPostProcessor(postProcessor);
      return factory;
    }
  }

  public static class CustomManagerFactory
      extends DefaultStandaloneContextAwareManagerFactory {
  }

  public static final class CustomNamingStrategyWroManagerFactory
      extends DefaultStandaloneContextAwareManagerFactory {
    public static final String PREFIX = "renamed";
    {
      setNamingStrategy(new NamingStrategy() {
        public String rename(final String originalName, final InputStream inputStream)
  public void shouldIgnoreChangesOfGroupsWhichAreNotPartOfTargetGroups() throws Exception {
          return PREFIX + originalName;
        }
      });
    }
  }

  public static final class CssUrlRewriterWroManagerFactory
      extends DefaultStandaloneContextAwareManagerFactory {
    @Override
    protected ProcessorsFactory newProcessorsFactory() {
      final SimpleProcessorsFactory factory = new SimpleProcessorsFactory();
      factory.addPreProcessor(new CssUrlRewritingProcessor());
      return factory;
    }
  }

  @Test
  public void shouldDetectIncrementalChange()
      throws Exception {
    victim = new Wro4jMojo() {
      @Override
      protected WroManagerFactory getManagerFactory() {
        return new WroManagerFactoryDecorator(super.getManagerFactory()) {
          @Override
          protected void onBeforeBuild(final Builder builder) {
            builder.setHashStrategy(mockHashStrategy);
          }
        };
      }
    };
    setUpMojo(victim);
    final String hashValue = "SomeHashValue";
    when(mockHashStrategy.getHash(Mockito.any(InputStream.class))).thenReturn(hashValue);
    when(mockBuildContext.isIncremental()).thenReturn(true);
    when(mockBuildContext.getValue(Mockito.anyString())).thenReturn(hashValue);
    victim.setIgnoreMissingResources(true);
      };
    assertTrue(victim.getTargetGroupsAsList().isEmpty());

    // incremental change detects change for all resources
    when(mockHashStrategy.getHash(Mockito.any(InputStream.class))).thenReturn("TotallyDifferentValue");
    assertFalse(victim.getTargetGroupsAsList().isEmpty());
  }

  @Test
  public void shouldDetectIncrementalChangeOfImportedCss()
      throws Exception {
    final String importResource = "imported.css";

    configureMojoForModelWithImportedCssResource(importResource);
    // incremental build detects no change
    assertTrue(victim.getTargetGroupsAsList().isEmpty());

    when(mockLocator.locate(Mockito.eq(importResource))).thenAnswer(answerWithContent("Changed"));
    assertFalse(victim.getTargetGroupsAsList().isEmpty());
  }

  private void configureMojoForModelWithImportedCssResource(final String importResource) throws Exception {
    final String parentResource = "parent.css";

    final WroModel model = new WroModel();
    model.addGroup(new Group("g1").addResource(Resource.create(parentResource)));
    when(mockLocator.locate(Mockito.anyString())).thenAnswer(answerWithContent(""));
    final String parentContent = String.format("@import url(%s)", importResource);
    when(mockLocator.locate(Mockito.eq(parentResource))).thenAnswer(answerWithContent(parentContent));

    victim = new Wro4jMojo() {
      @Override
      protected WroManagerFactory newWroManagerFactory()
          throws MojoExecutionException {
        final DefaultStandaloneContextAwareManagerFactory managerFactory = new DefaultStandaloneContextAwareManagerFactory();
        managerFactory.setUriLocatorFactory(mockLocatorFactory);
        managerFactory.setModelFactory(WroTestUtils.simpleModelFactory(model));
        return managerFactory;
      }
    };
    final HashStrategy hashStrategy = victim.getManagerFactory().create().getHashStrategy();
    setUpMojo(victim);

    final String importedInitialContent = "initial";

    when(mockLocator.locate(Mockito.eq(importResource))).thenAnswer(answerWithContent(importedInitialContent));
    when(mockBuildContext.isIncremental()).thenReturn(true);
    when(mockBuildContext.getValue(Mockito.eq(parentResource))).thenReturn(
        hashStrategy.getHash(new ByteArrayInputStream(parentContent.getBytes())));
    when(mockBuildContext.getValue(Mockito.eq(importResource))).thenReturn(
        hashStrategy.getHash(new ByteArrayInputStream(importedInitialContent.getBytes())));
    victim.setIgnoreMissingResources(true);
  }

  @Test
    assertEquals(0, victim.getTargetGroupsAsList().size());
    final String importResource = "imported.css";

    configureMojoForModelWithImportedCssResource(importResource);
    victim.setTargetGroups("g2");

    // incremental build detects no change
    assertTrue(victim.getTargetGroupsAsList().isEmpty());

    when(mockLocator.locate(Mockito.eq(importResource))).thenAnswer(answerWithContent("Changed"));
    assertTrue(victim.getTargetGroupsAsList().isEmpty());
  }

  @Test
  public void shouldReuseGroupNameMappingFileWithIncrementalBuild()
      throws Exception {
    final File groupNameMappingFile = WroUtil.createTempFile();

    final Resource g1Resource = spy(Resource.create("1.js"));
    try {
      final WroModel model = new WroModel();
      model.addGroup(new Group("g1").addResource(g1Resource));
      model.addGroup(new Group("g2").addResource(Resource.create("2.js")));
      victim = new Wro4jMojo() {
        @Override
        protected WroManagerFactory newWroManagerFactory()
            throws MojoExecutionException {
          final DefaultStandaloneContextAwareManagerFactory managerFactory = new DefaultStandaloneContextAwareManagerFactory();
          managerFactory.setUriLocatorFactory(WroTestUtils.createResourceMockingLocatorFactory());
          managerFactory.setModelFactory(WroTestUtils.simpleModelFactory(model));
          managerFactory.setNamingStrategy(new DefaultHashEncoderNamingStrategy());
          return managerFactory;
        }
      setUpMojo(victim);

      victim.setGroupNameMappingFile(groupNameMappingFile);

      assertEquals(2, victim.getTargetGroupsAsList().size());
      victim.execute();

      //Now mark it as incremental
      when(mockBuildContext.isIncremental()).thenReturn(true);

      final Properties groupNames = new Properties();
      groupNames.load(new FileInputStream(groupNameMappingFile));
      assertEquals(4, groupNames.entrySet().size());

      //change the uri of the resource from group a (equivalent to changing its content).
      when(g1Resource.getUri()).thenReturn("1a.js");

      assertEquals(1, victim.getTargetGroupsAsList().size());
      victim.execute();

      groupNames.load(new FileInputStream(groupNameMappingFile));
      // The number of persisted groupNames should still be unchanged, even though only a single group has been changed
      // after incremental build.
      assertEquals(4, groupNames.entrySet().size());
    } finally {
      FileUtils.deleteQuietly(groupNameMappingFile);
    }
  }

  /**
   * Test for the following scenario: when incremental build is performed, only changed resources are processed. Given
   * that there are two target groups, and a resource from only one group is changed incremental build should process
   * only that one group. However, if the targetFolder does not exist, all target groups must be processed.
   */
  @Test
  public void shouldProcessTargetGroupsWhenDestinationFolderDoesNotExist() throws Exception {
    victim = new Wro4jMojo() {
      @Override
      protected WroManagerFactory getManagerFactory() {
        return new WroManagerFactoryDecorator(super.getManagerFactory()) {
          @Override
          protected void onBeforeBuild(final Builder builder) {
            builder.setHashStrategy(mockHashStrategy);
          }
        };
      }
    };
    final String constantHash = "hash";
    when(mockHashStrategy.getHash(Mockito.any(InputStream.class))).thenReturn(constantHash);
    setUpMojo(victim);
    victim.setIgnoreMissingResources(true);

    final int totalGroups = 10;

    assertEquals(totalGroups, victim.getTargetGroupsAsList().size());

    when(mockBuildContext.isIncremental()).thenReturn(true);
    when(mockBuildContext.getValue(Mockito.anyString())).thenReturn(constantHash);

    //delete target folder
    destinationFolder.delete();

    assertEquals(totalGroups, victim.getTargetGroupsAsList().size());

    victim.doExecute();
  }

  private Answer answerWithContent(final String content) {
    return new Answer() {
      public InputStream answer(final InvocationOnMock invocation)
          throws Throwable {
        return new ByteArrayInputStream(content.getBytes());
      }
    };
  }

  /**
   * Verify that the plugin execution does not fail when one of the context folder is invalid.
   */
  @Test
  public void shouldUseMultipleContextFolders() throws Exception {
    final String defaultContextFolder = victim.getContextFoldersAsCSV();
    victim.setTargetGroups("contextRelative");

    victim.setContextFolder("invalid, " + defaultContextFolder);
    victim.doExecute();

    //reversed order should work the same
    victim.setContextFolder(defaultContextFolder + ", invalid");
    victim.doExecute();
  }

  @After
  public void tearDown()
      throws Exception {
    victim.clean();
    FileUtils.deleteDirectory(destinationFolder);
    FileUtils.deleteDirectory(cssDestinationFolder);
    FileUtils.deleteDirectory(jsDestinationFolder);
    FileUtils.deleteQuietly(extraConfigFile);
  }
}
>>>>>>> b552441e99002d2ffd91ba6e5cc91e498e134570
Solution content
/**
 * Copyright Alex Objelean
 */
package ro.isdc.wro.maven.plugin;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import java.io.ByteArrayInputStream;
import java.io.File;
import static org.junit.Assert.assertTrue;

import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.Reader;
import java.io.Writer;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Collection;
import java.util.Date;
import java.util.Properties;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicBoolean;

import org.apache.commons.io.FileUtils;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.MavenProject;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonatype.plexus.build.incremental.BuildContext;

import ro.isdc.wro.config.Context;
import ro.isdc.wro.manager.WroManager.Builder;
import ro.isdc.wro.manager.factory.WroManagerFactory;
import ro.isdc.wro.manager.factory.WroManagerFactoryDecorator;
import ro.isdc.wro.manager.factory.standalone.DefaultStandaloneContextAwareManagerFactory;
import ro.isdc.wro.maven.plugin.manager.factory.ConfigurableWroManagerFactory;
import ro.isdc.wro.model.WroModel;
import ro.isdc.wro.model.group.Group;
import ro.isdc.wro.model.resource.Resource;
import ro.isdc.wro.model.resource.locator.ResourceLocator;
import ro.isdc.wro.model.resource.locator.factory.ResourceLocatorFactory;
import ro.isdc.wro.model.resource.processor.ResourceProcessor;
import ro.isdc.wro.model.resource.processor.factory.ConfigurableProcessorsFactory;
import ro.isdc.wro.model.resource.processor.factory.ProcessorsFactory;
import ro.isdc.wro.model.resource.processor.factory.SimpleProcessorsFactory;
import ro.isdc.wro.model.resource.processor.impl.css.CssUrlRewritingProcessor;
import ro.isdc.wro.model.resource.support.hash.HashStrategy;
import ro.isdc.wro.model.resource.support.naming.ConfigurableNamingStrategy;
import ro.isdc.wro.model.resource.support.naming.DefaultHashEncoderNamingStrategy;
import ro.isdc.wro.model.resource.support.naming.FolderHashEncoderNamingStrategy;
import ro.isdc.wro.model.resource.support.naming.NamingStrategy;
import ro.isdc.wro.util.WroTestUtils;
import ro.isdc.wro.util.WroUtil;
import ro.isdc.wro.util.concurrent.TaskExecutor;


/**
 * Test class for {@link Wro4jMojo}
 *
 * @author Alex Objelean
 */
public class TestWro4jMojo {
  private static final Logger LOG = LoggerFactory.getLogger(TestWro4jMojo.class);
  @Mock
  private BuildContext mockBuildContext;
  @Mock
  private HashStrategy mockHashStrategy;
  @Mock
  private ResourceLocatorFactory mockLocatorFactory;
  @Mock
  private ResourceLocator mockLocator;
  private File cssDestinationFolder;
  private File jsDestinationFolder;
  private File destinationFolder;
  private File extraConfigFile;
  private Wro4jMojo victim;

  @Before
  public void setUp()
      throws Exception {
    MockitoAnnotations.initMocks(this);
    when(mockLocator.getInputStream()).thenAnswer(answerWithContent(""));
    Context.set(Context.standaloneContext());
    victim = new Wro4jMojo();
    setUpMojo(victim);
  }

  /**
   * Perform basic initialization with valid values of the provided mojo.
   */
  private void setUpMojo(final Wro4jMojo mojo)
      throws Exception {
    mojo.setIgnoreMissingResources(false);
    mojo.setMinimize(true);
    setWroWithValidResources();
    destinationFolder = new File(FileUtils.getTempDirectory(), "wroTemp-" + new Date().getTime());
    destinationFolder.mkdir();
    cssDestinationFolder = new File(FileUtils.getTempDirectory(), "wroTemp-css-" + new Date().getTime());
    destinationFolder.mkdir();
    jsDestinationFolder = new File(FileUtils.getTempDirectory(), "wroTemp-js-" + new Date().getTime());
    destinationFolder.mkdir();
    extraConfigFile = new File(FileUtils.getTempDirectory(), "extraConfig-" + new Date().getTime());
    extraConfigFile.createNewFile();
    mojo.setBuildDirectory(destinationFolder);
    mojo.setExtraConfigFile(extraConfigFile);
    mojo.setDestinationFolder(destinationFolder);
    mojo.setMavenProject(Mockito.mock(MavenProject.class));
    mojo.setBuildContext(mockBuildContext);
  }

  private void setWroFile(final String classpathResourceName)
      throws URISyntaxException {
    final URL url = getClass().getClassLoader().getResource(classpathResourceName);
    final File wroFile = new File(url.toURI());
    victim.setWroFile(wroFile);
    victim.setContextFolder(wroFile.getParentFile().getPath());
  }

  private void setWroWithValidResources()
      throws Exception {
    setWroFile("wro.xml");
  }

  private void setWroWithInvalidResources()
      throws Exception {
    setWroFile("wroWithInvalidResources.xml");
  }

  @Test
  public void testMojoWithPropertiesSetAndOneTargetGroup()
      throws Exception {
    victim.setTargetGroups("g1");
    victim.setIgnoreMissingResources(true);
    victim.execute();
  }

  @Test(expected = MojoExecutionException.class)
  public void shouldFailWhenInvalidResourcesAreUsed()
      throws Exception {
    victim.setIgnoreMissingResources(false);
    victim.execute();
  }

  @Test(expected = MojoExecutionException.class)
  public void testNoDestinationFolderSet()
      throws Exception {
    victim.setDestinationFolder(null);
    victim.execute();
  }

  @Test(expected = MojoExecutionException.class)
  public void testOnlyCssDestinationFolderSet()
      throws Exception {
    victim.setCssDestinationFolder(cssDestinationFolder);
    victim.setDestinationFolder(null);
    victim.execute();
  }

  @Test(expected = MojoExecutionException.class)
  public void testOnlyJsDestinationFolderSet()
      throws Exception {
    victim.setJsDestinationFolder(jsDestinationFolder);
    victim.setDestinationFolder(null);
    victim.execute();
  }

  @Test
  public void testJsAndCssDestinationFolderSet()
      throws Exception {
    victim.setIgnoreMissingResources(true);
    victim.setJsDestinationFolder(jsDestinationFolder);
    victim.setCssDestinationFolder(cssDestinationFolder);
    victim.execute();
  }

  @Test(expected = MojoExecutionException.class)
  public void cannotExecuteWhenInvalidResourcesPresentAndDoNotIgnoreMissingResources()
      throws Exception {
    setWroWithInvalidResources();
    victim.setIgnoreMissingResources(false);
    victim.execute();
  }

  @Test
  public void testWroXmlWithInvalidResourcesAndIgnoreMissingResourcesTrue()
      throws Exception {
    setWroWithInvalidResources();
    victim.setIgnoreMissingResources(true);
    victim.execute();
  }

  @Test(expected = MojoExecutionException.class)
  public void testMojoWithWroManagerFactorySet()
      throws Exception {
    victim.setWroManagerFactory(ExceptionThrowingWroManagerFactory.class.getName());
    victim.execute();
  }
  @Test(expected = MojoExecutionException.class)
  public void testInvalidMojoWithWroManagerFactorySet()
      throws Exception {
    victim.setWroManagerFactory("INVALID_CLASS_NAME");
    victim.execute();
  }

  @Test
  public void executeWithNullTargetGroupsProperty()
      throws Exception {
    victim.setIgnoreMissingResources(true);
    victim.setTargetGroups(null);
    victim.execute();
  }

  @Test(expected = MojoExecutionException.class)
  public void testMojoWithCustomManagerFactoryWithInvalidResourceAndNotIgnoreMissingResources()
      throws Exception {
    setWroWithInvalidResources();
    victim.setIgnoreMissingResources(false);
    victim.setWroManagerFactory(CustomManagerFactory.class.getName());
    victim.execute();
  }

  @Test
  public void testMojoWithCustomManagerFactoryWithInvalidResourceAndIgnoreMissingResources()
      throws Exception {
    setWroWithInvalidResources();
    victim.setIgnoreMissingResources(true);
    victim.setWroManagerFactory(CustomManagerFactory.class.getName());
    victim.execute();
  }

  @Test(expected = MojoExecutionException.class)
  public void testMojoWithConfigurableWroManagerFactory()
      throws Exception {
    setWroWithValidResources();
    victim.setIgnoreMissingResources(true);
    // by default a valid file is used, set null explicitly
    victim.setExtraConfigFile(null);
    victim.setWroManagerFactory(ConfigurableWroManagerFactory.class.getName());
    victim.execute();
  }

  @Test
  public void testMojoWithConfigurableWroManagerFactoryWithValidAndEmptyConfigFileSet()
      throws Exception {
    setWroWithValidResources();
    victim.setIgnoreMissingResources(true);
    victim.setWroManagerFactory(ConfigurableWroManagerFactory.class.getName());
    victim.execute();
  }

  @Test
  public void testMojoWithConfigurableWroManagerFactoryWithValidConfigFileSet()
      throws Exception {
    setWroWithValidResources();
    final String preProcessors = ConfigurableProcessorsFactory.PARAM_PRE_PROCESSORS + "=cssMin";
    FileUtils.write(extraConfigFile, preProcessors);

    victim.setIgnoreMissingResources(true);
    victim.setWroManagerFactory(ConfigurableWroManagerFactory.class.getName());
    victim.execute();
  }

  @Test
  public void shouldBeFasterWhenRunningProcessingInParallel() throws Exception {
    final long begin = System.currentTimeMillis();
    victim.setParallelProcessing(false);
    testMojoWithConfigurableWroManagerFactoryWithValidConfigFileSet();
    final long endSerial = System.currentTimeMillis();
    victim.setParallelProcessing(true);
    testMojoWithConfigurableWroManagerFactoryWithValidConfigFileSet();
    final long endParallel = System.currentTimeMillis();

    final long serial = endSerial - begin;
    final long parallel = endParallel - endSerial;
    LOG.info("serial took: {}ms", serial);
    LOG.info("parallel took: {}ms", parallel);
    assertTrue(serial > parallel);
  }

  @Test
  public void shouldUseTaskExecutorWhenRunningInParallel() throws Exception {
    final AtomicBoolean invoked = new AtomicBoolean();
    final TaskExecutor taskExecutor = new TaskExecutor() {
      @Override
      public void submit(final Collection> callables)
          throws Exception {
        invoked.set(true);
        super.submit(callables);
      }
    };
    victim.setTaskExecutor(taskExecutor);
    victim.setIgnoreMissingResources(true);

    victim.setParallelProcessing(false);
    victim.execute();
    assertFalse(invoked.get());

    victim.setParallelProcessing(true);
    victim.execute();
    assertTrue(invoked.get());
  }

  @Test
  public void testComputedAggregatedFolder()
      throws Exception {
    setWroWithValidResources();
    victim.setWroManagerFactory(CssUrlRewriterWroManagerFactory.class.getName());
    victim.setIgnoreMissingResources(true);
    final File cssDestinationFolder = new File(this.destinationFolder, "subfolder");
    cssDestinationFolder.mkdir();
    victim.setCssDestinationFolder(cssDestinationFolder);
    victim.execute();
  }

  @Test(expected = MojoExecutionException.class)
  public void testMojoWithConfigurableWroManagerFactoryWithInvalidPreProcessor()
      throws Exception {
    setWroWithValidResources();
    final String preProcessors = ConfigurableProcessorsFactory.PARAM_PRE_PROCESSORS + "=INVALID";
    FileUtils.write(extraConfigFile, preProcessors);

    victim.setIgnoreMissingResources(true);
    victim.setWroManagerFactory(ConfigurableWroManagerFactory.class.getName());
    victim.execute();
  }

  @Test
  public void shouldGenerateGroupMappingUsingNoOpNamingStrategy()
      throws Exception {
    setWroWithValidResources();

    final File groupNameMappingFile = new File(FileUtils.getTempDirectory(), "groupMapping-" + new Date().getTime());

    victim.setGroupNameMappingFile(groupNameMappingFile);
    victim.setIgnoreMissingResources(true);
    victim.execute();

    // verify
    final Properties groupNames = new Properties();
    groupNames.load(new FileInputStream(groupNameMappingFile));
    LOG.debug("groupNames: {}", groupNames);
    assertEquals("g1.js", groupNames.get("g1.js"));

    FileUtils.deleteQuietly(groupNameMappingFile);
  }

  @Test
  public void shouldGenerateGroupMappingUsingCustomNamingStrategy()
      throws Exception {
    setWroWithValidResources();

    final File groupNameMappingFile = new File(FileUtils.getTempDirectory(), "groupMapping-" + new Date().getTime());

    victim.setWroManagerFactory(CustomNamingStrategyWroManagerFactory.class.getName());
    victim.setGroupNameMappingFile(groupNameMappingFile);
    victim.setIgnoreMissingResources(true);
    victim.execute();

    // verify
    final Properties groupNames = new Properties();
    groupNames.load(new FileInputStream(groupNameMappingFile));
    LOG.debug("groupNames: {}", groupNames);
    Assert.assertEquals(CustomNamingStrategyWroManagerFactory.PREFIX + "g1.js", groupNames.get("g1.js"));

    FileUtils.deleteQuietly(groupNameMappingFile);
  }

  @Test
  public void shouldSkipSecondProcessingWhenIncrementalBuildEnabled() throws Exception {
    victim.setBuildContext(null);
    victim.setIncrementalBuildEnabled(true);
    testMojoWithConfigurableWroManagerFactoryWithValidConfigFileSet();
    testMojoWithConfigurableWroManagerFactoryWithValidConfigFileSet();
  }

  @Test
  public void shouldUseConfiguredNamingStrategy()
      throws Exception {
    setWroWithValidResources();

    final File extraConfigFile = new File(FileUtils.getTempDirectory(), "groupMapping-" + new Date().getTime());

    final Properties props = new Properties();
    // TODO create a properties builder
    props.setProperty(ConfigurableNamingStrategy.KEY, FolderHashEncoderNamingStrategy.ALIAS);
    props.list(new PrintStream(extraConfigFile));

    victim.setWroManagerFactory(ConfigurableWroManagerFactory.class.getName());
    victim.setExtraConfigFile(extraConfigFile);
    victim.setIgnoreMissingResources(true);
    victim.execute();

    FileUtils.deleteQuietly(extraConfigFile);
  }

  public static final class ExceptionThrowingWroManagerFactory
      extends DefaultStandaloneContextAwareManagerFactory {
    @Override
    protected ProcessorsFactory newProcessorsFactory() {
      final SimpleProcessorsFactory factory = new SimpleProcessorsFactory();
      final ResourceProcessor postProcessor = Mockito.mock(ResourceProcessor.class);
      try {
        Mockito.doThrow(new RuntimeException()).when(postProcessor).process(Mockito.any(Resource.class),
            Mockito.any(Reader.class), Mockito.any(Writer.class));
      } catch (final IOException e) {
        Assert.fail("never happen");
      }
      factory.addPostProcessor(postProcessor);
      return factory;
    }
  }

  public static class CustomManagerFactory
      extends DefaultStandaloneContextAwareManagerFactory {
  }

  public static final class CustomNamingStrategyWroManagerFactory
      extends DefaultStandaloneContextAwareManagerFactory {
    public static final String PREFIX = "renamed";
    {
      setNamingStrategy(new NamingStrategy() {
        public String rename(final String originalName, final InputStream inputStream)
            throws IOException {
          return PREFIX + originalName;
        }
      });
    }
  }

  public static final class CssUrlRewriterWroManagerFactory
      extends DefaultStandaloneContextAwareManagerFactory {
    @Override
    protected ProcessorsFactory newProcessorsFactory() {
      final SimpleProcessorsFactory factory = new SimpleProcessorsFactory();
      factory.addPreProcessor(new CssUrlRewritingProcessor());
      return factory;
    }
  }

  @Test
  public void shouldDetectIncrementalChange()
      throws Exception {
    victim = new Wro4jMojo() {
      @Override
      protected WroManagerFactory getManagerFactory() {
        return new WroManagerFactoryDecorator(super.getManagerFactory()) {
          @Override
          protected void onBeforeBuild(final Builder builder) {
            builder.setHashStrategy(mockHashStrategy);
          }
        };
      }
    };
    setUpMojo(victim);
    final String hashValue = "SomeHashValue";
    when(mockHashStrategy.getHash(Mockito.any(InputStream.class))).thenReturn(hashValue);
    when(mockBuildContext.isIncremental()).thenReturn(true);
    when(mockBuildContext.getValue(Mockito.anyString())).thenReturn(hashValue);
    victim.setIgnoreMissingResources(true);
    // incremental build detects no change
    assertTrue(victim.getTargetGroupsAsList().isEmpty());

    // incremental change detects change for all resources
    when(mockHashStrategy.getHash(Mockito.any(InputStream.class))).thenReturn("TotallyDifferentValue");
    assertFalse(victim.getTargetGroupsAsList().isEmpty());
  }

  @Test
  public void shouldDetectIncrementalChangeOfImportedCss()
      throws Exception {
    final String importResource = "imported.css";

    configureMojoForModelWithImportedCssResource(importResource);
    // incremental build detects no change
    assertTrue(victim.getTargetGroupsAsList().isEmpty());

    when(mockLocatorFactory.locate(Mockito.eq(importResource))).thenAnswer(answerWithContent("Changed"));
    assertFalse(victim.getTargetGroupsAsList().isEmpty());
  }

  private void configureMojoForModelWithImportedCssResource(final String importResource) throws Exception {
    final String parentResource = "parent.css";

    final WroModel model = new WroModel();
    model.addGroup(new Group("g1").addResource(Resource.create(parentResource)));
    when(mockLocatorFactory.locate(Mockito.anyString())).thenAnswer(answerWithContent(""));
    final String parentContent = String.format("@import url(%s)", importResource);
    when(mockLocatorFactory.locate(Mockito.eq(parentResource))).thenAnswer(answerWithContent(parentContent));

    victim = new Wro4jMojo() {
      @Override
      protected WroManagerFactory newWroManagerFactory()
          throws MojoExecutionException {
        final DefaultStandaloneContextAwareManagerFactory managerFactory = new DefaultStandaloneContextAwareManagerFactory();
        managerFactory.setLocatorFactory(mockLocatorFactory);
        managerFactory.setModelFactory(WroTestUtils.simpleModelFactory(model));
        return managerFactory;
      }
    };
    final HashStrategy hashStrategy = victim.getManagerFactory().create().getHashStrategy();
    setUpMojo(victim);

    final String importedInitialContent = "initial";

    when(mockLocatorFactory.locate(Mockito.eq(importResource))).thenAnswer(answerWithContent(importedInitialContent));
    when(mockBuildContext.isIncremental()).thenReturn(true);
    when(mockBuildContext.getValue(Mockito.eq(parentResource))).thenReturn(
        hashStrategy.getHash(new ByteArrayInputStream(parentContent.getBytes())));
    when(mockBuildContext.getValue(Mockito.eq(importResource))).thenReturn(
        hashStrategy.getHash(new ByteArrayInputStream(importedInitialContent.getBytes())));
    victim.setIgnoreMissingResources(true);
  }

  @Test
  public void shouldIgnoreChangesOfGroupsWhichAreNotPartOfTargetGroups() throws Exception {
    final String importResource = "imported.css";

    configureMojoForModelWithImportedCssResource(importResource);
    victim.setTargetGroups("g2");

    // incremental build detects no change
    assertTrue(victim.getTargetGroupsAsList().isEmpty());

    when(mockLocatorFactory.locate(Mockito.eq(importResource))).thenAnswer(answerWithContent("Changed"));
    assertTrue(victim.getTargetGroupsAsList().isEmpty());
  }

  @Test
  public void shouldReuseGroupNameMappingFileWithIncrementalBuild()
      throws Exception {
    final File groupNameMappingFile = WroUtil.createTempFile();

    final Resource g1Resource = spy(Resource.create("1.js"));
    try {
      final WroModel model = new WroModel();
      model.addGroup(new Group("g1").addResource(g1Resource));
      model.addGroup(new Group("g2").addResource(Resource.create("2.js")));
      victim = new Wro4jMojo() {
        @Override
        protected WroManagerFactory newWroManagerFactory()
            throws MojoExecutionException {
          final DefaultStandaloneContextAwareManagerFactory managerFactory = new DefaultStandaloneContextAwareManagerFactory();
          managerFactory.setLocatorFactory(WroTestUtils.createResourceMockingLocatorFactory());
          managerFactory.setModelFactory(WroTestUtils.simpleModelFactory(model));
          managerFactory.setNamingStrategy(new DefaultHashEncoderNamingStrategy());
          return managerFactory;
        }
      };
      setUpMojo(victim);

      victim.setGroupNameMappingFile(groupNameMappingFile);

      assertEquals(2, victim.getTargetGroupsAsList().size());
      victim.execute();

      //Now mark it as incremental
      when(mockBuildContext.isIncremental()).thenReturn(true);

      final Properties groupNames = new Properties();
      groupNames.load(new FileInputStream(groupNameMappingFile));
      assertEquals(4, groupNames.entrySet().size());

      //change the uri of the resource from group a (equivalent to changing its content).
      when(g1Resource.getUri()).thenReturn("1a.js");

      assertEquals(1, victim.getTargetGroupsAsList().size());
      victim.execute();

      groupNames.load(new FileInputStream(groupNameMappingFile));
      // The number of persisted groupNames should still be unchanged, even though only a single group has been changed
      // after incremental build.
      assertEquals(4, groupNames.entrySet().size());
    } finally {
      FileUtils.deleteQuietly(groupNameMappingFile);
    }
  }

  /**
   * Test for the following scenario: when incremental build is performed, only changed resources are processed. Given
   * that there are two target groups, and a resource from only one group is changed incremental build should process
   * only that one group. However, if the targetFolder does not exist, all target groups must be processed.
   */
  @Test
  public void shouldProcessTargetGroupsWhenDestinationFolderDoesNotExist() throws Exception {
    victim = new Wro4jMojo() {
      @Override
      protected WroManagerFactory getManagerFactory() {
        return new WroManagerFactoryDecorator(super.getManagerFactory()) {
          @Override
          protected void onBeforeBuild(final Builder builder) {
            builder.setHashStrategy(mockHashStrategy);
          }
        };
      }
    };
    final String constantHash = "hash";
    when(mockHashStrategy.getHash(Mockito.any(InputStream.class))).thenReturn(constantHash);
    setUpMojo(victim);
    victim.setIgnoreMissingResources(true);

    final int totalGroups = 10;

    assertEquals(totalGroups, victim.getTargetGroupsAsList().size());

    when(mockBuildContext.isIncremental()).thenReturn(true);
    when(mockBuildContext.getValue(Mockito.anyString())).thenReturn(constantHash);

    assertEquals(0, victim.getTargetGroupsAsList().size());

    //delete target folder
    destinationFolder.delete();

    assertEquals(totalGroups, victim.getTargetGroupsAsList().size());

    victim.doExecute();
  }

  private Answer answerWithContent(final String content) {
    return new Answer() {
      public InputStream answer(final InvocationOnMock invocation)
          throws Throwable {
        return new ByteArrayInputStream(content.getBytes());
      }
    };
  }

  /**
   * Verify that the plugin execution does not fail when one of the context folder is invalid.
   */
  @Test
  public void shouldUseMultipleContextFolders() throws Exception {
    final String defaultContextFolder = victim.getContextFoldersAsCSV();
    victim.setTargetGroups("contextRelative");

    victim.setContextFolder("invalid, " + defaultContextFolder);
    victim.doExecute();

    //reversed order should work the same
    victim.setContextFolder(defaultContextFolder + ", invalid");
    victim.doExecute();
  }

  @After
  public void tearDown()
      throws Exception {
    victim.clean();
    FileUtils.deleteDirectory(destinationFolder);
    FileUtils.deleteDirectory(cssDestinationFolder);
    FileUtils.deleteDirectory(jsDestinationFolder);
    FileUtils.deleteQuietly(extraConfigFile);
  }
}
File
TestWro4jMojo.java
Developer's decision
Combination
Kind of conflict
Class declaration
Comment
Import
Package declaration
Chunk
Conflicting content
  /**
   */
<<<<<<< HEAD
/**
 * Copyright Alex Objelean
 */
package ro.isdc.wro.runner;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.servlet.FilterConfig;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.kohsuke.args4j.CmdLineParser;
import org.kohsuke.args4j.Option;
import org.mockito.Mockito;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ro.isdc.wro.config.Context;
import ro.isdc.wro.config.factory.PropertyWroConfigurationFactory;
import ro.isdc.wro.config.jmx.WroConfiguration;
import ro.isdc.wro.extensions.model.factory.SmartWroModelFactory;
import ro.isdc.wro.extensions.processor.css.CssLintProcessor;
import ro.isdc.wro.extensions.processor.js.JsHintProcessor;
import ro.isdc.wro.extensions.processor.support.csslint.CssLintException;
import ro.isdc.wro.extensions.processor.support.linter.LinterException;
import ro.isdc.wro.http.support.DelegatingServletOutputStream;
import ro.isdc.wro.manager.WroManager;
import ro.isdc.wro.manager.factory.WroManagerFactory;
import ro.isdc.wro.manager.factory.standalone.DefaultStandaloneContextAwareManagerFactory;
import ro.isdc.wro.manager.factory.standalone.StandaloneContext;
import ro.isdc.wro.manager.factory.standalone.StandaloneContextAware;
import ro.isdc.wro.model.WroModel;
  private List getTargetGroupsAsList() {
   * @return a list containing all groups needs to be processed.
   */

      @Override
  private boolean parallelPreprocessing;
    } catch (final IOException e) {
import ro.isdc.wro.model.WroModelInspector;
import ro.isdc.wro.model.factory.WroModelFactory;
    } finally {
      watch.stop();
import ro.isdc.wro.model.resource.Resource;
import ro.isdc.wro.model.resource.ResourceType;
import ro.isdc.wro.model.resource.processor.ResourceProcessor;
import ro.isdc.wro.model.resource.processor.factory.ConfigurableProcessorsFactory;
import ro.isdc.wro.model.resource.processor.factory.ProcessorsFactory;
import ro.isdc.wro.util.StopWatch;
import ro.isdc.wro.util.io.UnclosableBufferedInputStream;


/**
 * Default command line runner. Interprets arguments and perform a processing.
 *
 * @author Alex Objelean
 * @since 1.3.4
 */
public class Wro4jCommandLineRunner {
  private static final Logger LOG = LoggerFactory.getLogger(Wro4jCommandLineRunner.class);
  private static String userDirectory = System.getProperty("user.dir");
  private final File defaultWroFile = newDefaultWroFile();

  @Option(name = "-m", aliases = {
    "--minimize"
  }, usage = "Turns on the minimization by applying compressor")
  private boolean minimize;
  @Option(name = "--parallel", usage = "Turns on the parallel preProcessing of resources. This value is false by default.")
  @Option(name = "--targetGroups", metaVar = "GROUPS", usage = "Comma separated value of the group names from wro.xml to process. If none is provided, all groups will be processed.")
  private String targetGroups;
  @Option(name = "-i", aliases = {
    "--ignoreMissingResources"
  }, usage = "Ignores missing resources")
  private boolean ignoreMissingResources;
  @Option(name = "--wroFile", metaVar = "PATH_TO_WRO_XML", usage = "The path to the wro model file. By default the model is searched inse the user current folder.")
  private final File wroFile = defaultWroFile;
  @Option(name = "--contextFolder", metaVar = "PATH", usage = "Folder used as a root of the context relative resources. By default this is the user current folder.")
  private final File contextFolder = new File(System.getProperty("user.dir"));
  @Option(name = "--destinationFolder", metaVar = "PATH", usage = "Where to store the processed result. By default uses the folder named [wro].")
  private File destinationFolder = new File(System.getProperty("user.dir"), "wro");
  @Option(name = "-c", aliases = {
    "--compressor", "--preProcessors"
  }, metaVar = "COMPRESSOR", usage = "Comma separated list of pre-processors")
  private String preProcessorsList;
  @Option(name = "--postProcessors", metaVar = "POST_PROCESSOR", usage = "Comma separated list of post-processors")
  private String postProcessorsList;



  public static void main(final String[] args)
      throws Exception {
    new Wro4jCommandLineRunner().doMain(args);
  }

  /**
   * @return the location where wro file is located by default. Default implementation uses current user directory.
   * @VisibleForTesting
   */
  protected File newDefaultWroFile() {
    return new File(userDirectory, "wro.xml");
  }


  /**
   * @return the location where wro configuration file is located by default. Default implementation uses current user directory.
   * @VisibleForTesting
   */
  protected File newWroConfigurationFile() throws IOException {
    return new File(userDirectory, "wro.properties");
  }

  /**
   * @param args
   */
  protected void doMain(final String[] args) {
    LOG.debug("arguments: " + Arrays.toString(args));
    final CmdLineParser parser = new CmdLineParser(this);
    parser.setUsageWidth(100);
    final StopWatch watch = new StopWatch();
    watch.start("processing");
    try {
      parser.parseArgument(args);
      LOG.debug("Options: {}", this);
      process();
    } catch (final Exception e) {
      System.err.println(e.getMessage() + "\n\n");
      System.err.println("=======================================");
      System.err.println("USAGE");
      System.err.println("=======================================");
      parser.printUsage(System.err);
      onRunnerException(e);
      System.err.println(e.getMessage());
    }
  }

      LOG.debug(watch.prettyPrint());
      LOG.info("Processing took: {}ms", watch.getLastTaskTimeMillis());
    }
  }

  /**
   * Exception handler.
   */
  protected void onRunnerException(final Exception e) {
    System.out.println(e.getMessage());
    System.exit(1); // non-zero exit code indicates there was an error
  }

  private void process() {
    try {
      Context.set(Context.standaloneContext());
      // create destinationFolder if needed
      if (!destinationFolder.exists()) {
        destinationFolder.mkdirs();
      }
      final Collection groupsAsList = getTargetGroupsAsList();
      for (final String group : groupsAsList) {
        for (final ResourceType resourceType : ResourceType.values()) {
          final String groupWithExtension = group + "." + resourceType.name().toLowerCase();
          processGroup(groupWithExtension, destinationFolder);
        }
      }
    if (targetGroups == null) {
      final WroModel model = getManagerFactory().create().getModelFactory().create();
      return new WroModelInspector(model).getGroupNames();
    }
    return Arrays.asList(targetGroups.split(","));
  }

  /**
   * Process a single group.
   *
   * @throws IOException
   *           if any IO related exception occurs.
   */
  private void processGroup(final String group, final File parentFoder)
      throws IOException {
    final ByteArrayOutputStream resultOutputStream = new ByteArrayOutputStream();
    InputStream resultInputStream = null;
    try {
      LOG.info("processing group: " + group);
      initContext(group, resultOutputStream);
      doProcess();

      // encode version & write result to file
      resultInputStream = new UnclosableBufferedInputStream(resultOutputStream.toByteArray());
      final File destinationFile = new File(parentFoder, rename(group, resultInputStream));
      destinationFile.createNewFile();
      // allow the same stream to be read again
      resultInputStream.reset();
      LOG.debug("Created file: {}", destinationFile.getName());

      final OutputStream fos = new FileOutputStream(destinationFile);
      // use reader to detect encoding
      IOUtils.copy(resultInputStream, fos);
      fos.close();
      // delete empty files
      if (destinationFile.length() == 0) {
        LOG.debug("No content found for group: {}", group);
        destinationFile.delete();
      } else {
        LOG.info("file size: {} -> {}bytes", destinationFile.getName(), destinationFile.length());
        LOG.info("{} ({}bytes) has been created!", destinationFile.getAbsolutePath(), destinationFile.length());
      }
    } finally {
      if (resultOutputStream != null) {
        resultOutputStream.close();
      }
      if (resultInputStream != null) {
        resultInputStream.close();
      }
    }
  }

  /**
   * Initialize the context for standalone execution.
   */
  private void initContext(final String group, final ByteArrayOutputStream resultOutputStream)
      throws IOException {
    // mock request
    final HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
    Mockito.when(request.getRequestURI()).thenReturn(group);
    // mock response
    final HttpServletResponse response = Mockito.mock(HttpServletResponse.class);
import ro.isdc.wro.manager.factory.WroManagerFactory;
    Mockito.when(response.getOutputStream()).thenReturn(new DelegatingServletOutputStream(resultOutputStream));

    // init context
    Context.set(Context.webContext(request, response, Mockito.mock(FilterConfig.class)), initWroConfiguration());
    Context.get().setAggregatedFolderPath(computeAggregatedFolderPath());
  }

  /**
   * Perform actual processing by delegating the process call to {@link WroManager}.
   * @throws IOException
   * @VisibleForTesting
   */
  void doProcess()
      throws IOException {
    // perform processing
    getManagerFactory().create().process();
  }

  private WroConfiguration initWroConfiguration()
      throws IOException {
    final PropertyWroConfigurationFactory factory = new PropertyWroConfigurationFactory(getWroConfigurationProperties());
    final WroConfiguration config = factory.create();
    //keep backward compatibility configuration of some config properties
    config.setParallelPreprocessing(parallelPreprocessing);
    return config;
  }

  /**
   * @return {@link Properties} object loaded from wro.properties file located in current user directory. This location
   *         is configurable. If the wro.properties file does not exist, the returned value will be an empty
   *         {@link Properties} object, equivalent to no config properties.
   * @throws IOException
   *           if loaded file is corrupt.
   */
  private Properties getWroConfigurationProperties()
      throws IOException {
    try {
      final Properties props = new Properties();
      final File configFile = newWroConfigurationFile();
      if (configFile.exists()) {
        LOG.debug("Using {} to load WroConfiguration.", configFile.getPath());
        props.load(new FileInputStream(configFile));
      } else {
        LOG.warn("{} does not exist. Using default configuration.", configFile.getPath());
      }
      return props;
    } catch (final IOException e) {
      LOG.error("Problem while loading WroConfiguration", e);
      throw e;
    }
  }

  /**
   * This implementation is similar to the one from Wro4jMojo. TODO: reuse if possible.
   */
  private String computeAggregatedFolderPath() {
    Validate.notNull(destinationFolder, "DestinationFolder cannot be null!");
    Validate.notNull(contextFolder, "ContextFolder cannot be null!");
    final File cssTargetFolder = destinationFolder;
    File rootFolder = null;
    if (cssTargetFolder.getPath().startsWith(contextFolder.getPath())) {
      rootFolder = contextFolder;
    }
    // compute aggregatedFolderPath
    String aggregatedFolderPath = null;
    if (rootFolder != null) {
      aggregatedFolderPath = StringUtils.removeStart(cssTargetFolder.getPath(), rootFolder.getPath());
    }
    LOG.debug("aggregatedFolderPath: {}", aggregatedFolderPath);
    return aggregatedFolderPath;
  }

  /**
   * Encodes a version using some logic.
   *
   * @param group
   *          the name of the resource to encode.
   * @param input
   *          the stream of the result content.
   * @return the name of the resource with the version encoded.
   */
  private String rename(final String group, final InputStream input)
      throws IOException {
    return getManagerFactory().create().getNamingStrategy().rename(group, input);
  }

  /**
   * This method will ensure that you have a right and initialized instance of
   * {@link StandaloneContextAware}.
   */
  private WroManagerFactory getManagerFactory() {
    final DefaultStandaloneContextAwareManagerFactory managerFactory = new DefaultStandaloneContextAwareManagerFactory();
    managerFactory.setProcessorsFactory(createProcessorsFactory());
    managerFactory.setModelFactory(createWroModelFactory());
    managerFactory.initialize(createStandaloneContext());
    //allow created manager to get injected immediately after creation
    return managerFactory;
  }

  private WroModelFactory createWroModelFactory() {
    // autodetect if user didn't specify explicitly the wro file path (aka default is used).
    final boolean autoDetectWroFile = defaultWroFile.getPath().equals(wroFile.getPath());
    return new SmartWroModelFactory().setWroFile(wroFile).setAutoDetectWroFile(autoDetectWroFile);
  }

  private ProcessorsFactory createProcessorsFactory() {
    final Properties props = new Properties();
    if (preProcessorsList != null) {
      props.setProperty(ConfigurableProcessorsFactory.PARAM_PRE_PROCESSORS, preProcessorsList);
    }
    if (postProcessorsList != null) {
      props.setProperty(ConfigurableProcessorsFactory.PARAM_POST_PROCESSORS, postProcessorsList);
    }
    return new ConfigurableProcessorsFactory() {
      @Override
      protected Map newPreProcessorsMap() {
        final Map map = super.newPreProcessorsMap();
        // override csslint & jsHint aliases
        map.put(CssLintProcessor.ALIAS, new RunnerCssLintProcessor());
        map.put(JsHintProcessor.ALIAS, new RunnerJsHintProcessor());
        return map;
      }
      protected Map newPostProcessorsMap() {
        final Map map = super.newPostProcessorsMap();
        // override csslint & jsHint aliases
        map.put(CssLintProcessor.ALIAS, new RunnerCssLintProcessor());
        map.put(JsHintProcessor.ALIAS, new RunnerJsHintProcessor());
        return map;
      }
    }.setProperties(props);
  }

  /**
   * Creates a {@link StandaloneContext} by setting properties passed after mojo is initialized.
   */
  private StandaloneContext createStandaloneContext() {
    final StandaloneContext runContext = new StandaloneContext();
    runContext.setContextFolder(contextFolder);
    runContext.setMinimize(minimize);
    runContext.setWroFile(wroFile);
    runContext.setIgnoreMissingResources(ignoreMissingResources);
    return runContext;
  }

  /**
   * @param destinationFolder
   *          the destinationFolder to set
   * @VisibleForTestOnly
   */
  void setDestinationFolder(final File destinationFolder) {
    this.destinationFolder = destinationFolder;
  }

  /**
   * Linter classes with custom exception handling.
   */
  private class RunnerCssLintProcessor
      extends CssLintProcessor {
    @Override
    protected void onCssLintException(final CssLintException e, final Resource resource) {
      super.onCssLintException(e, resource);
      System.err.println("The following resource: " + resource + " has " + e.getErrors().size() + " errors.");
      System.err.println(e.getErrors());
      onRunnerException(e);
    }
  }

  private class RunnerJsHintProcessor
      extends JsHintProcessor {
    @Override
    protected void onLinterException(final LinterException e, final Resource resource) {
      super.onLinterException(e, resource);
      System.err.println("The following resource: " + resource + " has " + e.getErrors().size() + " errors.");
      System.err.println(e.getErrors());
      onRunnerException(e);
    }
  }

}
=======
/**
 * Copyright Alex Objelean
 */
package ro.isdc.wro.runner;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
  @Option(name = "--postProcessors", metaVar = "POST_PROCESSOR", usage = "Comma separated list of post-processors")
  private String postProcessorsList;



import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.servlet.FilterConfig;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.kohsuke.args4j.CmdLineParser;
import org.kohsuke.args4j.Option;
import org.mockito.Mockito;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ro.isdc.wro.config.Context;
import ro.isdc.wro.config.factory.PropertyWroConfigurationFactory;
import ro.isdc.wro.config.jmx.WroConfiguration;
import ro.isdc.wro.extensions.model.factory.SmartWroModelFactory;
import ro.isdc.wro.extensions.processor.css.CssLintProcessor;
import ro.isdc.wro.extensions.processor.js.JsHintProcessor;
import ro.isdc.wro.extensions.processor.support.csslint.CssLintException;
import ro.isdc.wro.extensions.processor.support.linter.LinterException;
import ro.isdc.wro.http.support.DelegatingServletOutputStream;
import ro.isdc.wro.manager.WroManager;
import ro.isdc.wro.manager.factory.standalone.DefaultStandaloneContextAwareManagerFactory;
import ro.isdc.wro.manager.factory.standalone.StandaloneContext;
import ro.isdc.wro.manager.factory.standalone.StandaloneContextAware;
import ro.isdc.wro.model.WroModel;
import ro.isdc.wro.model.WroModelInspector;
import ro.isdc.wro.model.factory.WroModelFactory;
import ro.isdc.wro.model.resource.Resource;
import ro.isdc.wro.model.resource.ResourceType;
import ro.isdc.wro.model.resource.processor.ResourcePostProcessor;
import ro.isdc.wro.model.resource.processor.ResourcePreProcessor;
import ro.isdc.wro.model.resource.processor.factory.ConfigurableProcessorsFactory;
import ro.isdc.wro.model.resource.processor.factory.ProcessorsFactory;
import ro.isdc.wro.util.StopWatch;
import ro.isdc.wro.util.io.UnclosableBufferedInputStream;


/**
 * Default command line runner. Interprets arguments and perform a processing.
 *
 * @author Alex Objelean
 * @since 1.3.4
 */
public class Wro4jCommandLineRunner {
  private static final Logger LOG = LoggerFactory.getLogger(Wro4jCommandLineRunner.class);
  private static String userDirectory = System.getProperty("user.dir");
  private final File defaultWroFile = newDefaultWroFile();

  @Option(name = "-m", aliases = {
    "--minimize"
  }, usage = "Turns on the minimization by applying compressor")
  private boolean minimize;
  @Option(name = "--parallel", usage = "Turns on the parallel preProcessing of resources. This value is false by default.")
  private boolean parallelPreprocessing;
  @Option(name = "--targetGroups", metaVar = "GROUPS", usage = "Comma separated value of the group names from wro.xml to process. If none is provided, all groups will be processed.")
  private String targetGroups;
  @Option(name = "-i", aliases = {
    "--ignoreMissingResources"
  }, usage = "Ignores missing resources")
  private boolean ignoreMissingResources;
  @Option(name = "--wroFile", metaVar = "PATH_TO_WRO_XML", usage = "The path to the wro model file. By default the model is searched inse the user current folder.")
  private final File wroFile = defaultWroFile;
  @Option(name = "--contextFolder", metaVar = "PATH", usage = "Folder used as a root of the context relative resources. By default this is the user current folder.")
  private final File contextFolder = new File(System.getProperty("user.dir"));
  @Option(name = "--destinationFolder", metaVar = "PATH", usage = "Where to store the processed result. By default uses the folder named [wro].")
  private File destinationFolder = new File(System.getProperty("user.dir"), "wro");
  @Option(name = "-c", aliases = {
    "--compressor", "--preProcessors"
  }, metaVar = "COMPRESSOR", usage = "Comma separated list of pre-processors")
  private String preProcessorsList;
  private String computeAggregatedFolderPath() {
  public static void main(final String[] args)
      throws Exception {
    new Wro4jCommandLineRunner().doMain(args);
  }

  /**
   * @return the location where wro file is located by default. Default implementation uses current user directory.
   * @VisibleForTesting
   */
  protected File newDefaultWroFile() {
    return new File(userDirectory, "wro.xml");
  }


  /**
   * @return the location where wro configuration file is located by default. Default implementation uses current user directory.
   * @VisibleForTesting
   */
  protected File newWroConfigurationFile() throws IOException {
    return new File(userDirectory, "wro.properties");
  }

  /**
   * @param args
   */
  protected void doMain(final String[] args) {
    LOG.debug("arguments: " + Arrays.toString(args));
    final CmdLineParser parser = new CmdLineParser(this);
    parser.setUsageWidth(100);
    final StopWatch watch = new StopWatch();
    watch.start("processing");
    try {
      parser.parseArgument(args);
      LOG.debug("Options: {}", this);
      process();
    } catch (final Exception e) {
      System.err.println(e.getMessage() + "\n\n");
      System.err.println("=======================================");
      System.err.println("USAGE");
      System.err.println("=======================================");
      parser.printUsage(System.err);
      onRunnerException(e);
    } finally {
      watch.stop();
      LOG.debug(watch.prettyPrint());
      LOG.info("Processing took: {}ms", watch.getLastTaskTimeMillis());
    }
  }

  /**
   * Exception handler.
   */
  protected void onRunnerException(final Exception e) {
    System.out.println(e.getMessage());
    System.exit(1); // non-zero exit code indicates there was an error
  }

  private void process() {
    try {
      Context.set(Context.standaloneContext());
      // create destinationFolder if needed
      if (!destinationFolder.exists()) {
        destinationFolder.mkdirs();
      }
      final Collection groupsAsList = getTargetGroupsAsList();
      for (final String group : groupsAsList) {
        for (final ResourceType resourceType : ResourceType.values()) {
          final String groupWithExtension = group + "." + resourceType.name().toLowerCase();
          processGroup(groupWithExtension, destinationFolder);
        }
      }
    } catch (final IOException e) {
      System.err.println(e.getMessage());
    }
  }

  /**
   * @return a list containing all groups needs to be processed.
   */
  private List getTargetGroupsAsList() {
    if (targetGroups == null) {
      final WroModel model = getManagerFactory().create().getModelFactory().create();
      return new WroModelInspector(model).getGroupNames();
    }
    return Arrays.asList(targetGroups.split(","));
  }

  /**
   * Process a single group.
   *
   * @throws IOException
   *           if any IO related exception occurs.
   */
  private void processGroup(final String group, final File parentFoder)
      throws IOException {
    final ByteArrayOutputStream resultOutputStream = new ByteArrayOutputStream();
    InputStream resultInputStream = null;
    try {
      LOG.info("processing group: " + group);
      initContext(group, resultOutputStream);
      doProcess();

      // encode version & write result to file
      resultInputStream = new UnclosableBufferedInputStream(resultOutputStream.toByteArray());
      final File destinationFile = new File(parentFoder, rename(group, resultInputStream));
      destinationFile.createNewFile();
      // allow the same stream to be read again
      resultInputStream.reset();
      LOG.debug("Created file: {}", destinationFile.getName());

      final OutputStream fos = new FileOutputStream(destinationFile);
      // use reader to detect encoding
      IOUtils.copy(resultInputStream, fos);
      fos.close();
      // delete empty files
      if (destinationFile.length() == 0) {
        LOG.debug("No content found for group: {}", group);
        destinationFile.delete();
      } else {
        LOG.info("file size: {} -> {}bytes", destinationFile.getName(), destinationFile.length());
        LOG.info("{} ({}bytes) has been created!", destinationFile.getAbsolutePath(), destinationFile.length());
      }
    } finally {
      if (resultOutputStream != null) {
        resultOutputStream.close();
      }
      if (resultInputStream != null) {
        resultInputStream.close();
      }
    }
  }

  /**
   * Initialize the context for standalone execution.
   */
  private void initContext(final String group, final ByteArrayOutputStream resultOutputStream)
      throws IOException {
    // mock request
    final HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
    Mockito.when(request.getRequestURI()).thenReturn(group);
    // mock response
    final HttpServletResponse response = Mockito.mock(HttpServletResponse.class);
    Mockito.when(response.getOutputStream()).thenReturn(new DelegatingServletOutputStream(resultOutputStream));

    // init context
    Context.set(Context.webContext(request, response, Mockito.mock(FilterConfig.class)), initWroConfiguration());
    Context.get().setAggregatedFolderPath(computeAggregatedFolderPath());
  }

  /**
   * Perform actual processing by delegating the process call to {@link WroManager}.
   * @throws IOException
   * @VisibleForTesting
   */
  void doProcess()
      throws IOException {
    // perform processing
    getManagerFactory().create().process();
  }

  private WroConfiguration initWroConfiguration()
      throws IOException {
    final PropertyWroConfigurationFactory factory = new PropertyWroConfigurationFactory(getWroConfigurationProperties());
    final WroConfiguration config = factory.create();
    //keep backward compatibility configuration of some config properties
    config.setParallelPreprocessing(parallelPreprocessing);
    return config;
  }

  /**
   * @return {@link Properties} object loaded from wro.properties file located in current user directory. This location
   *         is configurable. If the wro.properties file does not exist, the returned value will be an empty
   *         {@link Properties} object, equivalent to no config properties.
   * @throws IOException
   *           if loaded file is corrupt.
   */
  private Properties getWroConfigurationProperties()
      throws IOException {
    try {
      final Properties props = new Properties();
      final File configFile = newWroConfigurationFile();
      if (configFile.exists()) {
        LOG.debug("Using {} to load WroConfiguration.", configFile.getPath());
        props.load(new FileInputStream(configFile));
      } else {
        LOG.warn("{} does not exist. Using default configuration.", configFile.getPath());
      }
      return props;
    } catch (final IOException e) {
      LOG.error("Problem while loading WroConfiguration", e);
      throw e;
    }
  }

  /**
   * This implementation is similar to the one from Wro4jMojo. TODO: reuse if possible.
   */
    Validate.notNull(destinationFolder, "DestinationFolder cannot be null!");
    Validate.notNull(contextFolder, "ContextFolder cannot be null!");
    final File cssTargetFolder = destinationFolder;
    File rootFolder = null;
    if (cssTargetFolder.getPath().startsWith(contextFolder.getPath())) {
      rootFolder = contextFolder;
    }
    // compute aggregatedFolderPath
    String aggregatedFolderPath = null;
    if (rootFolder != null) {
      aggregatedFolderPath = StringUtils.removeStart(cssTargetFolder.getPath(), rootFolder.getPath());
    }
    LOG.debug("aggregatedFolderPath: {}", aggregatedFolderPath);
    return aggregatedFolderPath;
  }

  /**
   * Encodes a version using some logic.
   *
   * @param group
   *          the name of the resource to encode.
   * @param input
   *          the stream of the result content.
   * @return the name of the resource with the version encoded.
   */
  private String rename(final String group, final InputStream input)
      throws IOException {
    return getManagerFactory().create().getNamingStrategy().rename(group, input);
  }

  /**
   * This method will ensure that you have a right and initialized instance of
   * {@link StandaloneContextAware}.
   */
  private WroManagerFactory getManagerFactory() {
    final DefaultStandaloneContextAwareManagerFactory managerFactory = new DefaultStandaloneContextAwareManagerFactory();
    managerFactory.setProcessorsFactory(createProcessorsFactory());
    managerFactory.setModelFactory(createWroModelFactory());
    managerFactory.initialize(createStandaloneContext());
    //allow created manager to get injected immediately after creation
    return managerFactory;
  }

  private WroModelFactory createWroModelFactory() {
    // autodetect if user didn't specify explicitly the wro file path (aka default is used).
    final boolean autoDetectWroFile = defaultWroFile.getPath().equals(wroFile.getPath());
    return new SmartWroModelFactory().setWroFile(wroFile).setAutoDetectWroFile(autoDetectWroFile);
  }

  private ProcessorsFactory createProcessorsFactory() {
    final Properties props = new Properties();
    if (preProcessorsList != null) {
      props.setProperty(ConfigurableProcessorsFactory.PARAM_PRE_PROCESSORS, preProcessorsList);
    }
    if (postProcessorsList != null) {
      props.setProperty(ConfigurableProcessorsFactory.PARAM_POST_PROCESSORS, postProcessorsList);
    }
    return new ConfigurableProcessorsFactory() {
      @Override
      protected Map newPreProcessorsMap() {
        final Map map = super.newPreProcessorsMap();
        // override csslint & jsHint aliases
        map.put(CssLintProcessor.ALIAS, new RunnerCssLintProcessor());
        map.put(JsHintProcessor.ALIAS, new RunnerJsHintProcessor());
        return map;
      }

      @Override
      protected Map newPostProcessorsMap() {
        final Map map = super.newPostProcessorsMap();
        // override csslint & jsHint aliases
        map.put(CssLintProcessor.ALIAS, new RunnerCssLintProcessor());
        map.put(JsHintProcessor.ALIAS, new RunnerJsHintProcessor());
        return map;
      }
    }.setProperties(props);
  }

  /**
   * Creates a {@link StandaloneContext} by setting properties passed after mojo is initialized.
   */
  private StandaloneContext createStandaloneContext() {
    final StandaloneContext runContext = new StandaloneContext();
    runContext.setContextFoldersAsCSV(contextFolder.getPath());
    runContext.setMinimize(minimize);
    runContext.setWroFile(wroFile);
    runContext.setIgnoreMissingResources(ignoreMissingResources);
    return runContext;
  }

  /**
   * @param destinationFolder
   *          the destinationFolder to set
   * @VisibleForTestOnly
  void setDestinationFolder(final File destinationFolder) {
    this.destinationFolder = destinationFolder;
  }

  /**
   * Linter classes with custom exception handling.
   */
  private class RunnerCssLintProcessor
      extends CssLintProcessor {
    @Override
    protected void onCssLintException(final CssLintException e, final Resource resource) {
      super.onCssLintException(e, resource);
      System.err.println("The following resource: " + resource + " has " + e.getErrors().size() + " errors.");
      System.err.println(e.getErrors());
      onRunnerException(e);
    }
  }

  private class RunnerJsHintProcessor
      extends JsHintProcessor {
    @Override
    protected void onLinterException(final LinterException e, final Resource resource) {
      super.onLinterException(e, resource);
      System.err.println("The following resource: " + resource + " has " + e.getErrors().size() + " errors.");
      System.err.println(e.getErrors());
      onRunnerException(e);
    }
  }

}
>>>>>>> b552441e99002d2ffd91ba6e5cc91e498e134570
Solution content
/**
 * Copyright Alex Objelean
 */
package ro.isdc.wro.runner;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.servlet.FilterConfig;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.kohsuke.args4j.CmdLineParser;
import org.kohsuke.args4j.Option;
import org.mockito.Mockito;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ro.isdc.wro.config.Context;
import ro.isdc.wro.config.factory.PropertyWroConfigurationFactory;
import ro.isdc.wro.config.jmx.WroConfiguration;
import ro.isdc.wro.extensions.model.factory.SmartWroModelFactory;
import ro.isdc.wro.extensions.processor.css.CssLintProcessor;
import ro.isdc.wro.extensions.processor.js.JsHintProcessor;
import ro.isdc.wro.extensions.processor.support.csslint.CssLintException;
import ro.isdc.wro.extensions.processor.support.linter.LinterException;
import ro.isdc.wro.http.support.DelegatingServletOutputStream;
import ro.isdc.wro.manager.WroManager;
import ro.isdc.wro.manager.factory.WroManagerFactory;
import ro.isdc.wro.manager.factory.standalone.DefaultStandaloneContextAwareManagerFactory;
import ro.isdc.wro.manager.factory.standalone.StandaloneContext;
import ro.isdc.wro.manager.factory.standalone.StandaloneContextAware;
import ro.isdc.wro.model.WroModel;
import ro.isdc.wro.model.WroModelInspector;
import ro.isdc.wro.model.factory.WroModelFactory;
import ro.isdc.wro.model.resource.Resource;
import ro.isdc.wro.model.resource.ResourceType;
import ro.isdc.wro.model.resource.processor.ResourceProcessor;
import ro.isdc.wro.model.resource.processor.factory.ConfigurableProcessorsFactory;
import ro.isdc.wro.model.resource.processor.factory.ProcessorsFactory;
import ro.isdc.wro.util.StopWatch;
import ro.isdc.wro.util.io.UnclosableBufferedInputStream;


/**
 * Default command line runner. Interprets arguments and perform a processing.
 *
 * @author Alex Objelean
 * @since 1.3.4
 */
public class Wro4jCommandLineRunner {
  private static final Logger LOG = LoggerFactory.getLogger(Wro4jCommandLineRunner.class);
  private static String userDirectory = System.getProperty("user.dir");
  private final File defaultWroFile = newDefaultWroFile();

  @Option(name = "-m", aliases = {
    "--minimize"
  }, usage = "Turns on the minimization by applying compressor")
  private boolean minimize;
  @Option(name = "--parallel", usage = "Turns on the parallel preProcessing of resources. This value is false by default.")
  private boolean parallelPreprocessing;
  @Option(name = "--targetGroups", metaVar = "GROUPS", usage = "Comma separated value of the group names from wro.xml to process. If none is provided, all groups will be processed.")
  private String targetGroups;
  @Option(name = "-i", aliases = {
    "--ignoreMissingResources"
  }, usage = "Ignores missing resources")
  private boolean ignoreMissingResources;
  @Option(name = "--wroFile", metaVar = "PATH_TO_WRO_XML", usage = "The path to the wro model file. By default the model is searched inse the user current folder.")
  private final File wroFile = defaultWroFile;
  @Option(name = "--contextFolder", metaVar = "PATH", usage = "Folder used as a root of the context relative resources. By default this is the user current folder.")
  private final File contextFolder = new File(System.getProperty("user.dir"));
  @Option(name = "--destinationFolder", metaVar = "PATH", usage = "Where to store the processed result. By default uses the folder named [wro].")
  private File destinationFolder = new File(System.getProperty("user.dir"), "wro");
  @Option(name = "-c", aliases = {
    "--compressor", "--preProcessors"
  }, metaVar = "COMPRESSOR", usage = "Comma separated list of pre-processors")
  private String preProcessorsList;
  @Option(name = "--postProcessors", metaVar = "POST_PROCESSOR", usage = "Comma separated list of post-processors")
  private String postProcessorsList;



  public static void main(final String[] args)
      throws Exception {
    new Wro4jCommandLineRunner().doMain(args);
  }

  /**
   * @return the location where wro file is located by default. Default implementation uses current user directory.
   * @VisibleForTesting
   */
  protected File newDefaultWroFile() {
    return new File(userDirectory, "wro.xml");
  }


  /**
   * @return the location where wro configuration file is located by default. Default implementation uses current user directory.
   * @VisibleForTesting
   */
  protected File newWroConfigurationFile() throws IOException {
    return new File(userDirectory, "wro.properties");
  }

  /**
   * @param args
   */
  protected void doMain(final String[] args) {
    LOG.debug("arguments: " + Arrays.toString(args));
    final CmdLineParser parser = new CmdLineParser(this);
    parser.setUsageWidth(100);
    final StopWatch watch = new StopWatch();
    watch.start("processing");
    try {
      parser.parseArgument(args);
      LOG.debug("Options: {}", this);
      process();
    } catch (final Exception e) {
      System.err.println(e.getMessage() + "\n\n");
      System.err.println("=======================================");
      System.err.println("USAGE");
      System.err.println("=======================================");
      parser.printUsage(System.err);
      onRunnerException(e);
    } finally {
      watch.stop();
      LOG.debug(watch.prettyPrint());
      LOG.info("Processing took: {}ms", watch.getLastTaskTimeMillis());
    }
  }

  /**
   * Exception handler.
   */
  protected void onRunnerException(final Exception e) {
    System.out.println(e.getMessage());
    System.exit(1); // non-zero exit code indicates there was an error
  }

  private void process() {
    try {
      Context.set(Context.standaloneContext());
      // create destinationFolder if needed
      if (!destinationFolder.exists()) {
        destinationFolder.mkdirs();
      }
      final Collection groupsAsList = getTargetGroupsAsList();
      for (final String group : groupsAsList) {
        for (final ResourceType resourceType : ResourceType.values()) {
          final String groupWithExtension = group + "." + resourceType.name().toLowerCase();
          processGroup(groupWithExtension, destinationFolder);
        }
      }
    } catch (final IOException e) {
      System.err.println(e.getMessage());
    }
  }

  /**
   * @return a list containing all groups needs to be processed.
   */
  private List getTargetGroupsAsList() {
    if (targetGroups == null) {
      final WroModel model = getManagerFactory().create().getModelFactory().create();
      return new WroModelInspector(model).getGroupNames();
    }
    return Arrays.asList(targetGroups.split(","));
  }

  /**
   * Process a single group.
   *
   * @throws IOException
   *           if any IO related exception occurs.
   */
  private void processGroup(final String group, final File parentFoder)
      throws IOException {
    final ByteArrayOutputStream resultOutputStream = new ByteArrayOutputStream();
    InputStream resultInputStream = null;
    try {
      LOG.info("processing group: " + group);
      initContext(group, resultOutputStream);
      doProcess();

      // encode version & write result to file
      resultInputStream = new UnclosableBufferedInputStream(resultOutputStream.toByteArray());
      final File destinationFile = new File(parentFoder, rename(group, resultInputStream));
      destinationFile.createNewFile();
      // allow the same stream to be read again
      resultInputStream.reset();
      LOG.debug("Created file: {}", destinationFile.getName());

      final OutputStream fos = new FileOutputStream(destinationFile);
      // use reader to detect encoding
      IOUtils.copy(resultInputStream, fos);
      fos.close();
      // delete empty files
      if (destinationFile.length() == 0) {
        LOG.debug("No content found for group: {}", group);
        destinationFile.delete();
      } else {
        LOG.info("file size: {} -> {}bytes", destinationFile.getName(), destinationFile.length());
        LOG.info("{} ({}bytes) has been created!", destinationFile.getAbsolutePath(), destinationFile.length());
      }
    } finally {
      if (resultOutputStream != null) {
        resultOutputStream.close();
      }
      if (resultInputStream != null) {
        resultInputStream.close();
      }
    }
  }

  /**
   * Initialize the context for standalone execution.
   */
  private void initContext(final String group, final ByteArrayOutputStream resultOutputStream)
      throws IOException {
    // mock request
    final HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
    Mockito.when(request.getRequestURI()).thenReturn(group);
    // mock response
    final HttpServletResponse response = Mockito.mock(HttpServletResponse.class);
    Mockito.when(response.getOutputStream()).thenReturn(new DelegatingServletOutputStream(resultOutputStream));

    // init context
    Context.set(Context.webContext(request, response, Mockito.mock(FilterConfig.class)), initWroConfiguration());
    Context.get().setAggregatedFolderPath(computeAggregatedFolderPath());
  }

  /**
   * Perform actual processing by delegating the process call to {@link WroManager}.
   * @throws IOException
   * @VisibleForTesting
   */
  void doProcess()
      throws IOException {
    // perform processing
    getManagerFactory().create().process();
  }

  private WroConfiguration initWroConfiguration()
      throws IOException {
    final PropertyWroConfigurationFactory factory = new PropertyWroConfigurationFactory(getWroConfigurationProperties());
    final WroConfiguration config = factory.create();
    //keep backward compatibility configuration of some config properties
    config.setParallelPreprocessing(parallelPreprocessing);
    return config;
  }

  /**
   * @return {@link Properties} object loaded from wro.properties file located in current user directory. This location
   *         is configurable. If the wro.properties file does not exist, the returned value will be an empty
   *         {@link Properties} object, equivalent to no config properties.
   * @throws IOException
   *           if loaded file is corrupt.
   */
  private Properties getWroConfigurationProperties()
      throws IOException {
    try {
      final Properties props = new Properties();
      final File configFile = newWroConfigurationFile();
      if (configFile.exists()) {
        LOG.debug("Using {} to load WroConfiguration.", configFile.getPath());
        props.load(new FileInputStream(configFile));
      } else {
        LOG.warn("{} does not exist. Using default configuration.", configFile.getPath());
      }
      return props;
    } catch (final IOException e) {
      LOG.error("Problem while loading WroConfiguration", e);
      throw e;
    }
  }

  /**
   * This implementation is similar to the one from Wro4jMojo. TODO: reuse if possible.
   */
  private String computeAggregatedFolderPath() {
    Validate.notNull(destinationFolder, "DestinationFolder cannot be null!");
    Validate.notNull(contextFolder, "ContextFolder cannot be null!");
    final File cssTargetFolder = destinationFolder;
    File rootFolder = null;
    if (cssTargetFolder.getPath().startsWith(contextFolder.getPath())) {
      rootFolder = contextFolder;
    }
    // compute aggregatedFolderPath
    String aggregatedFolderPath = null;
    if (rootFolder != null) {
      aggregatedFolderPath = StringUtils.removeStart(cssTargetFolder.getPath(), rootFolder.getPath());
    }
    LOG.debug("aggregatedFolderPath: {}", aggregatedFolderPath);
    return aggregatedFolderPath;
  }

  /**
   * Encodes a version using some logic.
   *
   * @param group
   *          the name of the resource to encode.
   * @param input
   *          the stream of the result content.
   * @return the name of the resource with the version encoded.
   */
  private String rename(final String group, final InputStream input)
      throws IOException {
    return getManagerFactory().create().getNamingStrategy().rename(group, input);
  }

  /**
   * This method will ensure that you have a right and initialized instance of
   * {@link StandaloneContextAware}.
   */
  private WroManagerFactory getManagerFactory() {
    final DefaultStandaloneContextAwareManagerFactory managerFactory = new DefaultStandaloneContextAwareManagerFactory();
    managerFactory.setProcessorsFactory(createProcessorsFactory());
    managerFactory.setModelFactory(createWroModelFactory());
    managerFactory.initialize(createStandaloneContext());
    //allow created manager to get injected immediately after creation
    return managerFactory;
  }

  private WroModelFactory createWroModelFactory() {
    // autodetect if user didn't specify explicitly the wro file path (aka default is used).
    final boolean autoDetectWroFile = defaultWroFile.getPath().equals(wroFile.getPath());
    return new SmartWroModelFactory().setWroFile(wroFile).setAutoDetectWroFile(autoDetectWroFile);
  }

  private ProcessorsFactory createProcessorsFactory() {
    final Properties props = new Properties();
    if (preProcessorsList != null) {
      props.setProperty(ConfigurableProcessorsFactory.PARAM_PRE_PROCESSORS, preProcessorsList);
    }
    if (postProcessorsList != null) {
      props.setProperty(ConfigurableProcessorsFactory.PARAM_POST_PROCESSORS, postProcessorsList);
    }
    return new ConfigurableProcessorsFactory() {
      @Override
      protected Map newPreProcessorsMap() {
        final Map map = super.newPreProcessorsMap();
        // override csslint & jsHint aliases
        map.put(CssLintProcessor.ALIAS, new RunnerCssLintProcessor());
        map.put(JsHintProcessor.ALIAS, new RunnerJsHintProcessor());
        return map;
      }

      @Override
      protected Map newPostProcessorsMap() {
        final Map map = super.newPostProcessorsMap();
        // override csslint & jsHint aliases
        map.put(CssLintProcessor.ALIAS, new RunnerCssLintProcessor());
        map.put(JsHintProcessor.ALIAS, new RunnerJsHintProcessor());
        return map;
      }
    }.setProperties(props);
  }

  /**
   * Creates a {@link StandaloneContext} by setting properties passed after mojo is initialized.
   */
  private StandaloneContext createStandaloneContext() {
    final StandaloneContext runContext = new StandaloneContext();
    runContext.setContextFoldersAsCSV(contextFolder.getPath());
    runContext.setMinimize(minimize);
    runContext.setWroFile(wroFile);
    runContext.setIgnoreMissingResources(ignoreMissingResources);
    return runContext;
  }

  /**
   * @param destinationFolder
   *          the destinationFolder to set
   * @VisibleForTestOnly
   */
  void setDestinationFolder(final File destinationFolder) {
    this.destinationFolder = destinationFolder;
  }

  /**
   * Linter classes with custom exception handling.
   */
  private class RunnerCssLintProcessor
      extends CssLintProcessor {
    @Override
    protected void onCssLintException(final CssLintException e, final Resource resource) {
      super.onCssLintException(e, resource);
      System.err.println("The following resource: " + resource + " has " + e.getErrors().size() + " errors.");
      System.err.println(e.getErrors());
      onRunnerException(e);
    }
  }

  private class RunnerJsHintProcessor
      extends JsHintProcessor {
    @Override
    protected void onLinterException(final LinterException e, final Resource resource) {
      super.onLinterException(e, resource);
      System.err.println("The following resource: " + resource + " has " + e.getErrors().size() + " errors.");
      System.err.println(e.getErrors());
      onRunnerException(e);
    }
  }

}
File
Wro4jCommandLineRunner.java
Developer's decision
Combination
Kind of conflict
Class declaration
Comment
Import
Package declaration