/*******************************************************************************
* Copyright (c) 2000, 2003 Advanced Systems Concepts, Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Advanced Systems Concepts - Initial api and implementation
* Yu You (Nokia) - Add ResouceManager support and exception error constances
*******************************************************************************/
package com.swtworkbench.community.xswt;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Decorations;
import org.eclipse.swt.widgets.Widget;
/**
* Class ClassBuilder. Uses reflection to construct SWT controls and other classes.
*
* <p>
* ClassBuilder has to take care of two different situations:
* <dl>
* <li>SWT Controls: each control must have a parent Composite</li>
* <li>SWT classes in graphics package: the classes require a Device as an
* argument in their constructors, for example, classes like Color and Font.
* </dl>
* </p>
* @author daveo
*/
public class ClassBuilder {
/*
* ERROR_NOT_CONTROL
*
* Parent is not a Control.
*/
public static final String ERROR_NOT_CONTROL = "parent is not Control";
/*
* ERROR_NOT_WIDGET
*
* Parent is not a Widget.
*/
public static final String ERROR_NOT_WIDGET = "parent is not Widget";
/*
* ERROR_NOT_COMPOSITE
*
* Parent is not a Compisite.
*/
public static final String ERROR_NOT_COMPOSITE = "parent is not Composite";
/*
* ERROR_NOT_DECORATION
*
* Parent is not a Decoration.
*/
public static final String ERROR_NOT_DECORATION = "parent is not Decoration";
private static ClassBuilder builder = null;
/**
* Method getDefault. Return the default ClassBuilder object.
* @return
*/
public static ClassBuilder getDefault() {
if (builder == null)
builder = new ClassBuilder();
return builder;
}
public ClassBuilder() {
/*
if (importExtensionPointPackages) {
if (defaultPackageImports != null) {
for (int i = 0; i < defaultPackageImports.size(); i++) {
importPackage((String)defaultPackageImports.get(i));
}
}
if (defaultClassImports != null) {
for (int i = 0; i < defaultClassImports.size(); i++) {
importClass((String)defaultClassImports.get(i));
}
}
// XswtPlugin plugin = XswtPlugin.getDefault();
// if (plugin != null) {
// plugin.importPackages(this);
// plugin.importClasses(this);
// }
}
*/
}
private ClassLoader classLoader = getClass().getClassLoader();
public ClassLoader getClassLoader() {
return classLoader;
}
public void setClassLoader(ClassLoader classloader) {
this.classLoader = classloader;
resolvedClasses = new HashMap();
}
/*
private static List defaultPackageImports;
public static void addDefaultPackageImports(String pack) {
if (defaultPackageImports == null) {
defaultPackageImports = new ArrayList();
}
defaultPackageImports.add(pack);
}
private static List defaultClassImports;
public static void addDefaultClassImports(String className) {
if (defaultClassImports == null) {
defaultClassImports = new ArrayList();
}
defaultClassImports.add(className);
}
*/
// Remember the imported packages here
private List imports = new ArrayList();
/**
* Method importPackage. Adds packageName to the list of packages that
* will be searched at object construction time.
*
* @param packageName The fully-qualified package name as a String
*/
public void importPackage(String packageName) {
imports.add(packageName);
}
/**
* Method importClass. Ensures that the simple class name resolves this fully qualified class name.
*
* @param className The fully-qualified class name as a String
*/
public void importClass(String className) {
int pos = className.lastIndexOf('.');
String simpleName = className.substring(pos + 1);
try {
Class result = getClass(className);
importClass(simpleName, result);
} catch (Exception e) {};
}
/**
* Method importClass. Ensures that the simple class name resolves this fully qualified class name.
*
* @param className The fully-qualified class name as a String
*/
public void importClass(String simpleName, Class klass) {
resolvedClasses.put(simpleName, klass);
}
/**
* Method imports. Return the list of imports in this XSWT file.
*
* @return List the imports list.
*/
public List imports() {
return imports;
}
/*
* Utility methods for the common case of constructing from the current classpath
*/
// Map class name String --> resolved Class object
private Map resolvedClasses = new HashMap();
/**
* Method getClass. Resolve a simple class name (not necessarily
* fully-qualified) to its Class object.
*
* @param className The name of the class to resolve
* @return The Class object represented by className
* @throws XSWTException if the Class could not be found
*/
public Class getClass(String className) throws XSWTException {
Class result = null;
// See if we've resolved this one before...
result = (Class) resolvedClasses.get(className);
if (result != null) return result;
// See if we can construct a FQN for the class and resolve it...
Iterator i = imports.iterator();
while (i.hasNext()) {
StringBuffer packageName = new StringBuffer((String) i.next());
packageName.append(".");
packageName.append(className);
String fullyQualifiedName = packageName.toString();
try {
result = classLoader.loadClass(fullyQualifiedName);
} catch (Exception e) { result = null;};
if (result != null) {
resolvedClasses.put(className, result);
StyleParser.registerClassConstants(result);
return result;
}
}
// See if it's already a fully-qualified class name
try {
result = classLoader.loadClass(className);
} catch (Throwable t) {}
if (result != null) {
resolvedClasses.put(className, result);
StyleParser.registerClassConstants(result);
return result;
}
throw new XSWTException("Unable to resolve class: " + className + ". Check the import node for the necessary package name");
}
/**
* Method constructControl. Construct an SWT control using reflection on
* its class name.<p>
*
* This method constructs a control given either its simple or
* fully-qualified class name, its parent, and its style bits. If a simple
* class name is passed, the package in which the class is found must
* have been previously passed using the importPackage() method.<p>
*
* The ClassBuilder object caches the Class objects of resolved classes,
* so that a search for the desired Class object is only performed once.
*
* @param className The simple or fully-qualified class name as a String.
* @param parent The Parent "control"
* @param style Style bits
* @return The constructed Widget or ControlEditor.
* @throws XSWTException If something went wrong. Wraps the actual exception.
*/
public Object constructControl(Class klass, Object parent, int style) throws XSWTException {
try {
Constructor constructor = null;
// FIXME: It should be possible to rewrite this as 2 loops: one over
// the 2-arg cases, and the second over the one-arg cases
if (parent != null) {
// First we try all the 2-arg constructor possibilities
try {
if (!(parent instanceof Decorations)) throw new Exception(ERROR_NOT_DECORATION);
constructor = klass.getDeclaredConstructor(
new Class[] {Decorations.class, Integer.TYPE});
} catch (Exception e) {
try {
if (!(parent instanceof Composite)) throw new Exception(ERROR_NOT_COMPOSITE);
constructor = klass.getDeclaredConstructor(
new Class[] {Composite.class, Integer.TYPE});
} catch (Exception e0) {
try {
if (!(parent instanceof Control)) throw new Exception(ERROR_NOT_CONTROL);
constructor = klass.getDeclaredConstructor(
new Class[] {Control.class, Integer.TYPE});
} catch (Exception e1) {
try {
if (!(parent instanceof Widget)) throw new Exception(ERROR_NOT_WIDGET);
constructor = klass.getDeclaredConstructor(
new Class[] {Widget.class, Integer.TYPE});
} catch (Exception e2) {
try {
constructor = klass.getDeclaredConstructor(
new Class[] {parent.getClass(), Integer.TYPE});
} catch (Exception e3) {
// Now we try the 1-arg constructor possibilities
try {
if (!(parent instanceof Control)) throw new Exception(ERROR_NOT_CONTROL);
constructor = klass.getDeclaredConstructor(
new Class[] {Control.class});
} catch (Exception e4) {
try {
if (!(parent instanceof Widget)) throw new Exception(ERROR_NOT_WIDGET);
constructor = klass.getDeclaredConstructor(
new Class[] {Widget.class});
} catch (Exception e5) {
constructor = klass.getDeclaredConstructor(
new Class[] {parent.getClass()});
}
}
}
}
}
}
}
} else {
// If parent is null, use a 0-arg constructor.
// This should currently only happen if called from within x:extends
constructor = klass.getDeclaredConstructor(
new Class[] {});
}
// Now try to actually construct the object
if (constructor.getParameterTypes().length == 2) {
return constructor.newInstance(new Object[]{parent, new Integer(style)});
}
if (constructor.getParameterTypes().length == 1) {
return constructor.newInstance(new Object[]{parent});
}
return constructor.newInstance(new Object[] {});
} catch (Throwable t) {
throw new XSWTException(t);
}
}
/**
* Clean up resources
*/
public void dispose() {
imports.clear();
resolvedClasses.clear();
}
}