/* * Copyright 2012 Joseph Spencer. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.spencernetdevelopment; import java.util.ArrayList; import java.util.List; /** * This class represents several assets that are meant to be grouped together * into a single file as part of a transaction. It is assumed that instances * will interact with a GroupedAssetTransactionManager. * <br/> * <b>Note: </b>This class is not thread safe. * * * <p><b>Main points to consider</b></p> * <ol> * <li> * The transaction allows the same asset to occur multiple times within a group. * This facilitates the case where a script or other resource may wish to be * duplicated multiple times to avoid repetition in code, and other * unforeseeable usages. It is therefore imperative that clients add only the * resources intended. * </li> * <li> * The transaction <i>must</i> be closed before any processing may be performed * on the underlying data. * </li> * <li> * The identifier is a unique representation in an MD5 format for any given set * of URLs. It is assumed that the identifier will be used in the filename of * the resulting asset once processing is complete. * </li> * <li> * List is used to hold assets in the transaction, and ordering is guaranteed * to occur as given. * </li> * </ol> * * @author Joseph Spencer */ public class GroupedAssetTransaction { private final String type; private final boolean isCompressed; private final boolean shouldWrapJsInClosure; private final List<String> urls = new ArrayList<>(); private boolean isOpenForAdditions=true; private String identifier; /** * Sets isCompressed to true.<br/> * See: {@link GroupedAssetTransaction#GroupedAssetTransaction(java.lang.String, boolean)} * @param type */ public GroupedAssetTransaction(String type) throws NullPointerException, IllegalArgumentException { this(type, true); } /** * @param type one of 'js', 'css' * @param compress Sets whether or not resources should be compressed. * @throws NullPointerException If type is null. * @throws IllegalArgumentException If type is <i>not</i> one of: js, css */ public GroupedAssetTransaction(String type, boolean compress) throws NullPointerException, IllegalArgumentException { this(type, compress, false); } public GroupedAssetTransaction( String type, boolean compress, boolean shouldWrapJsInClosure ) throws NullPointerException, IllegalArgumentException { if(type == null){ throw new NullPointerException("type was null"); } switch(type){ case "css": case "js": break; default: throw new IllegalArgumentException("Unknown type: "+type); } this.type=type; this.isCompressed=compress; this.shouldWrapJsInClosure=shouldWrapJsInClosure; } /** * Adds a URL to the underlying list. * * @param url * @throws IllegalStateException If the group is closed. * @throws NullPointerException If url is null. * @throws IllegalArgumentException If url is empty. */ public void addURL(String url) throws IllegalStateException, NullPointerException, IllegalArgumentException { if(!isOpenForAdditions){ throw new IllegalStateException( "This grouped resource is closed for additional urls. "+ "Couldn't add the following url: "+url ); } if(url == null){ throw new NullPointerException("url was null"); } if(url.trim().isEmpty()){ throw new IllegalArgumentException("The given url was empty."); } urls.add(url); } /** * Returns a URL friendly identifier that is unique to any set of URLs. * * @throws IllegalStateException If the transaction hasn't been closed. * @return an identifier based on the following algorithm: * <ol> * <li> * Create a new string as the value of the number of URLs to identify. * </li> * <li>Join URLs to the new string prefixing each with '_'</li> * <li>Hash the result with MD5</li> * </ol> */ public String getIdentifier() throws IllegalStateException { close(); if(identifier == null){ identifier = ""+urls.size(); for(String url:urls){ identifier += "_"; identifier += url; } identifier = CryptoUtils.md5(identifier); } return identifier; } /** * @return the type of this group, typically one of: 'js' or 'css'. */ public String getType(){ return type; } /** * @throws IllegalStateException When the close method has not been called, * @return A fixed length array of the URLs provided. */ public String[] toArray() throws IllegalStateException { close(); return urls.toArray(new String[]{}); } /** * Used to determine if the transaction is closed. * @return */ public boolean isClosed(){ return !isOpenForAdditions; } /** * Used to determine if the assets are to be compressed. * @return */ public boolean isCompressed(){ return isCompressed; } /** * Used for groups representing javascript. When true, the result should be * surrounded with a closure block to contain potential global variables. * @return */ public boolean shouldWrapJsInClosure(){ return shouldWrapJsInClosure; } /** * Ensures that URLs have been added to this group, and updates the internal * state to allow further processing. * @throws IllegalStateException If no URLs have been added to this group. */ public void close() throws IllegalStateException { if(!isOpenForAdditions){ return; } if(urls.size() < 1){ throw new IllegalStateException( "Can't close this resource, no urls have been added."); } isOpenForAdditions=false; } }