/**
* Copyright 2014 55 Minutes (http://www.55minutes.com)
*
* 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 fiftyfive.wicket.js.locator;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.List;
import fiftyfive.wicket.js.JavaScriptDependencySettings;
import org.apache.wicket.request.resource.ResourceReference;
import org.apache.wicket.WicketRuntimeException;
import org.apache.wicket.util.resource.IResourceStream;
import org.apache.wicket.util.resource.ResourceStreamNotFoundException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Parses JavaScript files for
* <a href="http://getsprockets.org/">sprocket</a> dependencies
* and recurses to find
* the dependencies of those, until all are discovered.
*
* @since 2.0
*/
public class SprocketsDependencyCollector
{
private static final Logger LOGGER = LoggerFactory.getLogger(
SprocketsDependencyCollector.class
);
private JavaScriptDependencyLocator locator;
private SprocketsParser parser;
/**
* Constructs a instance that will use the given
* JavaScriptDependencyLocator for recursively loading JavaScript files
* that are found as dependencies.
*/
public SprocketsDependencyCollector(JavaScriptDependencyLocator locator,
SprocketsParser parser)
{
super();
this.locator = locator;
this.parser = parser;
}
/**
* Parse the given JavaScript file stream for sprockets dependency
* declarations. For each dependency that is found, recursively invoke
* the JavaScriptDependencyLocator to locate the dependency and parse it
* for its dependencies, and so on. All the scripts that are found as a
* result of this process will be added to the specified
* DependencyCollection.
*
* @param ref The location of the JavaScript file to parse
* @param stream An opened stream of the JavaScript file to parse
* @param dependencies Target collection to which all dependencies will
* be added
*/
public void collectDependencies(ResourceReference ref,
IResourceStream stream,
DependencyCollection dependencies)
{
if(null == stream) return;
// Parse the resource, looking for Sprocket dependency declarations
LOGGER.debug("Parsing: {}", ref.getName());
List<Sprocket> sprockets = parseSprocketsFromStream(stream);
// After parsing is complete, loop through what we found and
// process their dependencies recursively.
for(Sprocket sp : sprockets)
{
if(sp.isLibrary())
{
this.locator.findLibraryScripts(sp.getPath(), dependencies);
}
else
{
Class<?> scope = ref.getScope();
this.locator.findResourceScripts(
scope,
concatPaths(ref.getName(), sp.getPath()),
dependencies
);
}
}
}
/**
* Parse the given stream and translate any i/o exceptions into
* WicketRuntimeException. Close the stream cleanly no matter what.
*/
private List<Sprocket> parseSprocketsFromStream(IResourceStream stream)
{
try
{
InputStream is = stream.getInputStream();
String enc = JavaScriptDependencySettings.get().getEncoding();
return this.parser.parseSprockets(
new BufferedReader(new InputStreamReader(is, enc))
);
}
catch(IOException ioe)
{
throw new WicketRuntimeException(ioe);
}
catch(ResourceStreamNotFoundException rsnfe)
{
throw new WicketRuntimeException(rsnfe);
}
finally
{
try { stream.close(); } catch(Exception ignore) {}
}
}
/**
* Returns a new path by resolving it relative to an original path.
*/
private String concatPaths(String orig, String relative)
{
if(relative.startsWith("./"))
{
relative = relative.substring(2);
}
if(null == orig || orig.indexOf("/") == -1)
{
return relative;
}
return orig.substring(0, orig.lastIndexOf("/") + 1) + relative;
}
}