/**
* 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.util.List;
import java.util.ArrayList;
import java.net.URL;
import java.net.JarURLConnection;
import java.util.jar.JarFile;
import java.util.jar.JarEntry;
import java.util.ArrayList;
import java.util.Enumeration;
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 JarProtocolScanner
implements ProtocolScanner
{
/**
* <p>Scans the entire jar at the specified 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 jarUrl )
throws IOException
{
// NOTE: we are not closing this connection, because it was causing cached jars to be closed. If this is causing problems with other
// jars that are not on the classpath, we may need to use JarInputStream to do this work.
// get the jar from the url.
JarURLConnection conn = (JarURLConnection)jarUrl.openConnection();
JarFile jarFile = conn.getJarFile();
// for each of the enties, get a jar entry and add it to the tree.
Enumeration<JarEntry> jarEntryEnum = jarFile.entries();
while( jarEntryEnum.hasMoreElements() ) {
JarEntry entry = jarEntryEnum.nextElement();
insertEntryNodes( rootNode, entry );
}
}
/**
* <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);
}
}
}
}