/**
* Copyright 2011 meltmedia
*
* 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 org.xchain.framework.scanner;
import java.io.IOException;
import java.net.URL;
import java.net.MalformedURLException;
import java.util.jar.JarFile;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.Map;
import java.util.TreeMap;
/**
* <p>This protocol scanner scans the protocol "jar:" and returns the root node of the scan. The root node of the scan
* can be used to discover other nodes in the jar.</p>
*
* @author Christian Trimble
*/
public class ZipProtocolScanner
implements ProtocolScanner
{
/**
* <p>Scans the entries in a Weblogic Zip URL and adds all of the entries of that jar into the tree rooted at rootNode. Missing
* directory nodes will be added.</p>
*
* @param rootNode the root node of the scan.
* @param jarUrl the url of the jar with entries to be added to the scan node.
*/
public void scan( ScanNode rootNode, URL zipUrl )
throws IOException
{
// get the file url for this zip url.
URL fileUrl = zipUrlToFileUrl(zipUrl);
JarInputStream in = null;
try {
in = new JarInputStream(fileUrl.openStream());
JarEntry entry = null;
while( (entry = in.getNextJarEntry()) != null ) {
insertEntryNodes( rootNode, entry );
}
}
catch( Exception e ) {
}
finally {
if( in != null ) {
try { in.close(); } catch( Exception e ) {}
}
}
}
/**
* <p>Converts a url for the root of a zip file (ends with !/) and returns the file for that jar.</p>
*/
URL zipUrlToFileUrl( URL zipRootUrl )
throws MalformedURLException
{
return new URL("file:"+zipRootUrl.getPath().replaceAll("\\!/\\Z", ""));
}
/**
* <p>Adds an entry to the root node for a JarEntry. This method is declared to be protected, so that test cases an interact with it.
* In general, this method should be treaded as private.</p>
*
* @param rootNode the root node of the scan.
* @param entry the jar entry to place under the root node.
*/
void insertEntryNodes( ScanNode rootNode, JarEntry entry )
{
String name = entry.getName();
// TODO: verify that this works on windows.
String[] parts = name.split("\\/");
int depth = 0;
ScanNode parentNode = rootNode;
// create any missing directory nodes.
for( int i = 0; i < parts.length - 1; i++ ) {
ScanNode currNode = parentNode.getChildMap().get(parts[i]);
if( currNode == null ) {
currNode = new ScanNode();
currNode.setResourceName("".equals(parentNode.getResourceName())?parts[i]:parentNode.getResourceName()+"/"+parts[i]);
currNode.setName(parts[i]);
currNode.setDirectory(true);
currNode.setFile(false);
parentNode.getChildMap().put(parts[i], currNode);
}
else {
currNode.setDirectory(true);
}
parentNode = currNode;
}
// ASSERT: parentNode is now the parent node of this entry.
// create the entry node if it is not already in the scan tree.
ScanNode currNode = parentNode.getChildMap().get(parts[parts.length-1]);
if( currNode == null ) {
currNode = new ScanNode();
currNode.setResourceName("".equals(parentNode.getResourceName())?parts[parts.length-1]:parentNode.getResourceName()+"/"+parts[parts.length-1]);
currNode.setName(parts[parts.length-1]);
currNode.setDirectory(entry.isDirectory()?true:false);
currNode.setFile(entry.isDirectory()?false:true);
parentNode.getChildMap().put(parts[parts.length-1], currNode);
}
else {
if( entry.isDirectory() ) {
currNode.setDirectory(true);
}
else {
currNode.setFile(true);
}
}
}
}