/*
* (c) Copyright 2010-2011 AgileBirds
*
* This file is part of OpenFlexo.
*
* OpenFlexo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenFlexo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenFlexo. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openflexo.fib.utils;
import java.beans.PropertyChangeSupport;
import java.net.URL;
import java.text.Collator;
import java.util.Collections;
import java.util.Comparator;
import java.util.Hashtable;
import java.util.List;
import java.util.Vector;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import javax.swing.Icon;
import org.openflexo.toolbox.ClassScope;
import org.openflexo.toolbox.HasPropertyChangeSupport;
import org.openflexo.toolbox.StringUtils;
public class LoadedClassesInfo implements HasPropertyChangeSupport {
private static final Logger logger = Logger.getLogger(LoadedClassesInfo.class.getPackage().getName());
static ClassLoader appLoader = ClassLoader.getSystemClassLoader();
static ClassLoader currentLoader = LoadedClassesInfo.class.getClassLoader();
static ClassLoader[] loaders = new ClassLoader[] { appLoader, currentLoader };
private static LoadedClassesInfo instance;
static {
appLoader = ClassLoader.getSystemClassLoader();
currentLoader = LoadedClassesInfo.class.getClassLoader();
if (appLoader != currentLoader) {
loaders = new ClassLoader[] { appLoader, currentLoader };
} else {
loaders = new ClassLoader[] { appLoader };
}
instance = new LoadedClassesInfo();
}
private Hashtable<Package, PackageInfo> packages;
private Vector<PackageInfo> packageList;
private boolean needsReordering = true;
public Vector<ClassInfo> matchingClasses = new Vector<ClassInfo>();
private Hashtable<String, Vector<ClassInfo>> classesForName;
private PropertyChangeSupport pcSupport;
public static LoadedClassesInfo instance() {
return instance;
}
public static LoadedClassesInfo instance(Class aClass) {
if (aClass != null) {
ClassInfo ci = instance.registerClass(aClass);
instance.setFilteredClassName(aClass.getSimpleName());
// instance.setFilteredPackageName(aClass.getPackage().getName());
instance.setSelectedClassInfo(ci);
} else {
instance.setFilteredPackageName("*");
instance.setFilteredClassName("");
instance.setSelectedClassInfo(null);
}
return instance;
}
private LoadedClassesInfo() {
pcSupport = new PropertyChangeSupport(this);
classesForName = new Hashtable<String, Vector<ClassInfo>>();
packages = new Hashtable<Package, PackageInfo>() {
@Override
public synchronized PackageInfo put(Package key, PackageInfo value) {
PackageInfo returned = super.put(key, value);
needsReordering = true;
pcSupport.firePropertyChange("packages", null, null);
return returned;
};
};
for (Package p : Package.getPackages()) {
registerPackage(p);
}
final Class<?>[] classes = ClassScope.getLoadedClasses(loaders);
for (Class<?> cls : classes) {
registerClass(cls);
String className = cls.getName();
URL classLocation = ClassScope.getClassLocation(cls);
// System.out.println("Registered class: " + className + " from " +classLocation);
}
}
@Override
public PropertyChangeSupport getPropertyChangeSupport() {
return pcSupport;
}
@Override
public String getDeletedProperty() {
return null;
}
public List<PackageInfo> getPackages() {
if (needsReordering) {
packageList = new Vector<PackageInfo>();
for (Package p : packages.keySet()) {
packageList.add(packages.get(p));
}
Collections.sort(packageList, new Comparator<PackageInfo>() {
@Override
public int compare(PackageInfo o1, PackageInfo o2) {
return Collator.getInstance().compare(o1.packageName, o2.packageName);
}
});
needsReordering = false;
}
return packageList;
}
private PackageInfo registerPackage(Package p) {
PackageInfo returned = packages.get(p);
if (returned == null) {
packages.put(p, returned = new PackageInfo(p));
}
return returned;
}
private ClassInfo registerClass(Class c) {
if (c.getPackage() == null) {
logger.warning("No package for class " + c);
return null;
}
PackageInfo p = registerPackage(c.getPackage());
logger.fine("Register class " + c);
if (!c.isMemberClass() && !c.isAnonymousClass() && !c.isLocalClass()) {
ClassInfo returned = p.classes.get(c);
if (returned == null) {
p.classes.put(c, returned = new ClassInfo(c));
logger.fine("Store " + returned + " in package " + p.packageName);
}
return returned;
} else if (c.isMemberClass()) {
// System.out.println("Member class: "+c+" of "+c.getDeclaringClass());
ClassInfo parentClass = registerClass(c.getEnclosingClass());
if (parentClass != null) {
ClassInfo returned = parentClass.declareMember(c);
return returned;
}
return null;
} else {
// System.out.println("Ignored class: "+c);
return null;
}
}
public class PackageInfo implements HasPropertyChangeSupport {
public String packageName;
private Hashtable<Class, ClassInfo> classes = new Hashtable<Class, ClassInfo>() {
@Override
public synchronized ClassInfo put(Class key, ClassInfo value) {
ClassInfo returned = super.put(key, value);
needsReordering = true;
pcSupport.firePropertyChange("classes", null, null);
return returned;
};
};
private Vector<ClassInfo> classesList;
private boolean needsReordering = true;
private PropertyChangeSupport pcSupport;
public PackageInfo(Package aPackage) {
pcSupport = new PropertyChangeSupport(this);
packageName = aPackage.getName();
}
@Override
public PropertyChangeSupport getPropertyChangeSupport() {
return pcSupport;
}
@Override
public String getDeletedProperty() {
// TODO Auto-generated method stub
return null;
}
public List<ClassInfo> getClasses() {
if (needsReordering) {
classesList = new Vector<ClassInfo>();
for (Class c : classes.keySet()) {
classesList.add(classes.get(c));
}
Collections.sort(classesList, new Comparator<ClassInfo>() {
@Override
public int compare(ClassInfo o1, ClassInfo o2) {
return Collator.getInstance().compare(o1.className, o2.className);
}
});
needsReordering = false;
}
return classesList;
}
public boolean isFiltered() {
if (getFilteredPackageName() == null || StringUtils.isEmpty(getFilteredPackageName())) {
return false;
}
if (packageName.startsWith(getFilteredPackageName())) {
return false;
}
String patternString = getFilteredPackageName();
if (patternString.startsWith("*")) {
patternString = "." + getFilteredPackageName();
}
try {
Pattern pattern = Pattern.compile(patternString);
Matcher matcher = pattern.matcher(packageName);
return !matcher.find();
} catch (PatternSyntaxException e) {
logger.warning("PatternSyntaxException: " + patternString);
return false;
}
}
public Icon getIcon() {
return FIBIconLibrary.PACKAGE_ICON;
}
}
public class ClassInfo implements HasPropertyChangeSupport {
private Class clazz;
public String className;
public String packageName;
public String fullQualifiedName;
private Hashtable<Class, ClassInfo> memberClasses = new Hashtable<Class, ClassInfo>() {
@Override
public synchronized ClassInfo put(Class key, ClassInfo value) {
ClassInfo returned = super.put(key, value);
needsReordering = true;
pcSupport.firePropertyChange("memberClasses", null, null);
return returned;
};
};
private Vector<ClassInfo> memberClassesList;
private boolean needsReordering = true;
private PropertyChangeSupport pcSupport;
public ClassInfo(Class aClass) {
pcSupport = new PropertyChangeSupport(this);
Vector<ClassInfo> listOfClassesWithThatName = classesForName.get(aClass.getSimpleName());
if (listOfClassesWithThatName == null) {
classesForName.put(aClass.getSimpleName(), listOfClassesWithThatName = new Vector<ClassInfo>());
}
listOfClassesWithThatName.add(this);
className = aClass.getSimpleName();
packageName = aClass.getPackage().getName();
fullQualifiedName = aClass.getName();
clazz = aClass;
logger.fine("Instanciate new ClassInfo for " + aClass);
}
@Override
public PropertyChangeSupport getPropertyChangeSupport() {
return pcSupport;
}
@Override
public String getDeletedProperty() {
return null;
}
protected ClassInfo declareMember(Class c) {
ClassInfo returned = memberClasses.get(c);
if (returned == null) {
memberClasses.put(c, returned = new ClassInfo(c));
needsReordering = true;
logger.fine(toString() + ": declare member: " + returned);
}
return returned;
}
public List<ClassInfo> getMemberClasses() {
if (needsReordering) {
memberClassesList = new Vector<ClassInfo>();
for (Class c : memberClasses.keySet()) {
memberClassesList.add(memberClasses.get(c));
}
Collections.sort(memberClassesList, new Comparator<ClassInfo>() {
@Override
public int compare(ClassInfo o1, ClassInfo o2) {
return Collator.getInstance().compare(o1.className, o2.className);
}
});
needsReordering = false;
}
return memberClassesList;
}
public Class getRepresentedClass() {
return clazz;
}
@Override
public String toString() {
return "ClassInfo[" + clazz.getName() + "]";
}
public Icon getIcon() {
if (clazz.isEnum()) {
return FIBIconLibrary.ENUM_ICON;
}
if (clazz.isInterface()) {
return FIBIconLibrary.INTERFACE_ICON;
}
return FIBIconLibrary.CLASS_ICON;
}
}
private String filteredPackageName = "*";
private String filteredClassName = "";
public String getFilteredPackageName() {
return filteredPackageName;
}
public void setFilteredPackageName(String filter) {
if (filter == null || !filter.equals(this.filteredPackageName)) {
this.filteredPackageName = filter;
updateMatchingClasses();
}
}
public String getFilteredClassName() {
return filteredClassName;
}
public void setFilteredClassName(String filteredClassName) {
if (filteredClassName == null || !filteredClassName.equals(this.filteredClassName)) {
this.filteredClassName = filteredClassName;
/*Vector<Class> foundClasses = new Vector<Class>();
try {
Class foundClass = Class.forName(getFilteredPackageName()+"."+filteredClassName);
foundClasses.add(foundClass);
logger.info("Found class "+foundClass);
} catch (ClassNotFoundException e) {
}
for (Package p : packages.keySet()) {
try {
Class foundClass = Class.forName(p.getName()+"."+filteredClassName);
foundClasses.add(foundClass);
logger.info("Found class "+foundClass);
} catch (ClassNotFoundException e) {
}
}
for (Class c : foundClasses) {
registerClass(c);
}*/
updateMatchingClasses();
}
}
public void search() {
Vector<Class> foundClasses = new Vector<Class>();
try {
Class foundClass = Class.forName(getFilteredPackageName() + "." + filteredClassName);
foundClasses.add(foundClass);
logger.info("Found class " + foundClass);
} catch (ClassNotFoundException e) {
}
for (Package p : packages.keySet()) {
try {
Class foundClass = Class.forName(p.getName() + "." + filteredClassName);
foundClasses.add(foundClass);
logger.info("Found class " + foundClass);
} catch (ClassNotFoundException e) {
}
}
for (Class c : foundClasses) {
registerClass(c);
}
updateMatchingClasses();
}
private void updateMatchingClasses() {
matchingClasses.clear();
if (!StringUtils.isEmpty(filteredClassName)) {
String patternString = filteredClassName;
if (patternString.startsWith("*")) {
patternString = "." + filteredClassName;
}
try {
Vector<ClassInfo> exactMatches = new Vector<ClassInfo>();
if (classesForName.get(filteredClassName) != null) {
exactMatches = classesForName.get(filteredClassName);
matchingClasses.addAll(exactMatches);
}
Pattern pattern = Pattern.compile(patternString);
for (String s : classesForName.keySet()) {
Matcher matcher = pattern.matcher(s);
if (matcher.find()) {
for (ClassInfo potentialMatch : classesForName.get(s)) {
PackageInfo packageInfo = registerPackage(potentialMatch.clazz.getPackage());
if (!packageInfo.isFiltered()) {
if (!exactMatches.contains(potentialMatch)) {
matchingClasses.add(potentialMatch);
// System.out.println("Found "+potentialMatch);
}
}
}
}
}
} catch (PatternSyntaxException e) {
logger.warning("PatternSyntaxException: " + patternString);
}
if (matchingClasses.size() == 1) {
setSelectedClassInfo(matchingClasses.firstElement());
}
}
instance.pcSupport.firePropertyChange("packages", null, null);
pcSupport.firePropertyChange("matchingClasses", null, null);
}
private ClassInfo selectedClassInfo;
public ClassInfo getSelectedClassInfo() {
return selectedClassInfo;
}
public void setSelectedClassInfo(ClassInfo selectedClassInfo) {
if (selectedClassInfo != this.selectedClassInfo) {
ClassInfo oldSelectedClassInfo = this.selectedClassInfo;
logger.info("setSelectedClassInfo with " + selectedClassInfo);
this.selectedClassInfo = selectedClassInfo;
// if (selectedClassInfo != null) setFilteredClassName(selectedClassInfo.className);
pcSupport.firePropertyChange("selectedClassInfo", oldSelectedClassInfo, selectedClassInfo);
}
}
}