/*******************************************************************************
* Copyright (c) 2007 Exadel, Inc. and Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is made available under the terms of the
* Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Exadel, Inc. and Red Hat, Inc. - initial API and implementation
******************************************************************************/
package org.jboss.tools.common.model.util;
import java.io.*;
import java.util.*;
import java.util.jar.*;
import org.jboss.tools.common.model.XModelObject;
import org.jboss.tools.common.model.XModelObjectConstants;
import org.jboss.tools.common.model.plugin.ModelPlugin;
public class AccessibleJava implements ISimpleTree {
private static AccessibleJava instance = null;
public static interface IContextPackageProvider {
public void setObject(XModelObject object);
public String getContextPackage();
}
public static AccessibleJava getInstance() {
if(instance == null) instance = new AccessibleJava();
return instance;
}
public static String getClassPath() {
StringBuffer sb = new StringBuffer();
String[] s = new String[0];
for (int i = 0; i < s.length; i++) sb.append(File.pathSeparator).append(s[i]);
return sb.toString();
}
private Map<String,JarFile> jarsmap = null;
private Map<String,SortedSet<String>> map = null;
private File[] folders = null;
private HashSet<String> checkedpackages = new HashSet<String>();
private AccessibleJava() {
load();
}
private String getJavaLib() {
String rt = System.getProperty("java.home") + "/lib/rt.jar"; //$NON-NLS-1$ //$NON-NLS-2$
return rt;
}
protected String extension() {
return ".class"; //$NON-NLS-1$
}
private void load() {
map = new HashMap<String,SortedSet<String>>();
jarsmap = new HashMap<String,JarFile>();
addJars(getJavaLib());
String paths = getClassPath();
addJars(paths);
Iterator c = jarsmap.values().iterator();
while(c.hasNext()) loadJar((JarFile)c.next());
jarsmap = null;
addFolders(paths);
}
private void addJars(String paths) {
StringTokenizer st = new StringTokenizer(paths, File.pathSeparator);
while(st.hasMoreTokens()) {
String t = st.nextToken().trim();
try {
addJar(new JarFile(t));
} catch (IOException e) {
ModelPlugin.getPluginLog().logError("addJars:" + e.getClass().getName()); //$NON-NLS-1$
}
}
}
private void addJar(JarFile jf) throws IOException {
String n = new File(jf.getName()).getCanonicalPath();
if(jarsmap.get(n) != null) return;
jarsmap.put(n, jf);
addJarManifest(jf);
}
private void loadJar(JarFile jar) {
Enumeration e = jar.entries();
while(e.hasMoreElements()) {
Object o = e.nextElement();
if(!(o instanceof JarEntry)) continue;
JarEntry je = (JarEntry)o;
register(je.getName(), map);
}
try {
jar.close();
} catch (IOException exc) {
ModelPlugin.getPluginLog().logError("AccessibleJava:Cannot close jar."); //$NON-NLS-1$
}
}
private static String[] serviceroots = {"CVS/", "META-INF/", ".svn"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
private void register(String path, Map<String,SortedSet<String>> map) {
if(path == null || path.indexOf("$") >= 0 || path.equals(XModelObjectConstants.SEPARATOR)) return; //$NON-NLS-1$
for (int j = 0; j < serviceroots.length; j++)
if(path.indexOf(serviceroots[j]) >= 0) return;
boolean isPackage = path.endsWith(XModelObjectConstants.SEPARATOR);
if(!(isPackage || path.endsWith(extension()))) return;
if(path.startsWith(XModelObjectConstants.SEPARATOR)) path = path.substring(1);
path = path.substring(0, path.length() - ((isPackage) ? 1 : 6)).replace('/', '.');
register0(path, isPackage, map);
}
public String[] getContent(String packagename) {
checkfolders(packagename);
SortedSet<String> list = map.get(packagename);
return (list == null) ? null : list.toArray(new String[list.size()]);
}
private void register0(String path, boolean isPackage, Map<String,SortedSet<String>> map) {
String parent, name;
int i = path.lastIndexOf('.');
if(i > 0) {
parent = path.substring(0, i);
name = path.substring(i + 1);
} else {
parent = "%root%"; //$NON-NLS-1$
name = path;
}
if(!isJavaName(name)) return;
SortedSet<String> list = map.get(parent);
if(list == null) {
list = new TreeSet<String>();
map.put(parent, list);
}
String pname = name + "."; //$NON-NLS-1$
if(list.contains(name) || list.contains(pname)) return;
list.add((isPackage) ? pname : name);
if(i > 0) register0(parent, true, map);
}
boolean isJavaName(String name) {
if(name.length() == 0) return false;
if(!Character.isJavaIdentifierStart(name.charAt(0))) return false;
for (int i = 1; i < name.length(); i++) {
if(!Character.isJavaIdentifierPart(name.charAt(i))) return false;
}
return true;
}
private void addFolders(String paths) {
StringTokenizer st = new StringTokenizer(paths, File.pathSeparator);
ArrayList<File> l = new ArrayList<File>();
while(st.hasMoreTokens()) {
String t = st.nextToken().trim();
if(t.length() == 0 || t.indexOf('~') >= 0) continue;
File f = new File(t);
if(f.isDirectory()) l.add(f);
}
folders = l.toArray(new File[l.size()]);
for (int i = 0; i < folders.length; i++) {
File[] fs = folders[i].listFiles();
if(fs != null) for (int j = 0; j < fs.length; j++) {
if(fs[j].isDirectory()) register(fs[j].getName() + XModelObjectConstants.SEPARATOR, map);
}
}
}
private void checkfolders(String pkg) {
if(checkedpackages.contains(pkg)) return;
checkedpackages.add(pkg);
for (int i = 0; i < folders.length; i++) loadfolder(folders[i], pkg);
int i = pkg.lastIndexOf('.');
if(i >= 0) checkfolders(pkg.substring(0, i));
}
private void loadfolder(File f, String pkg) {
String p = pkg.replace('.', '/');
File pf = new File(f.getAbsolutePath() + '/' + p);
if(!pf.exists()) return;
File[] fs = pf.listFiles();
if(fs != null) for (int i = 0; i < fs.length; i++) {
String n = fs[i].getName();
if(fs[i].isDirectory()) register(p + '/' + n + '/', map);
else if(n.endsWith(extension())) register(p + '/' + n, map);
}
}
private void addJarManifest(JarFile jf) throws IOException {
Manifest m = jf.getManifest();
Attributes as = m.getMainAttributes();
String classpath = as.getValue("Class-Path"); //$NON-NLS-1$
if(classpath == null) return;
String r = new File(jf.getName()).getParentFile().getCanonicalPath() + File.separatorChar;
StringTokenizer st = new StringTokenizer(classpath, " "); //$NON-NLS-1$
StringBuffer sb = new StringBuffer();
while(st.hasMoreElements()) {
String t = st.nextToken().trim();
if(t.length() > 0) sb.append(r).append(t).append(File.pathSeparator);
}
addJars(sb.toString());
}
}