diff options
Diffstat (limited to 'sca-java-2.x/tags/2.0-M5.1-RC1/modules/common-java/src/main/java/org/apache/tuscany/sca/common')
7 files changed, 1227 insertions, 0 deletions
diff --git a/sca-java-2.x/tags/2.0-M5.1-RC1/modules/common-java/src/main/java/org/apache/tuscany/sca/common/java/classloader/ClassLoaderDelegate.java b/sca-java-2.x/tags/2.0-M5.1-RC1/modules/common-java/src/main/java/org/apache/tuscany/sca/common/java/classloader/ClassLoaderDelegate.java new file mode 100644 index 0000000000..522880711c --- /dev/null +++ b/sca-java-2.x/tags/2.0-M5.1-RC1/modules/common-java/src/main/java/org/apache/tuscany/sca/common/java/classloader/ClassLoaderDelegate.java @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.tuscany.sca.common.java.classloader; + +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Enumeration; +import java.util.List; + +import org.apache.tuscany.sca.common.java.collection.CompoundEnumeration; + +/** + * + * @tuscany.spi.extension.asclient + * + */ +public class ClassLoaderDelegate extends ClassLoader { + private final List<ClassLoader> classLoaders = new ArrayList<ClassLoader>(); + + /** + * @param parent The parent classloaders + * @param loaders A list of classloaders to be used to load classes or resources + */ + public ClassLoaderDelegate(ClassLoader parent, ClassLoader... loaders) { + super(parent); + if (loaders != null) { + for (ClassLoader cl : loaders) { + if (cl != null && cl != parent && !classLoaders.contains(cl)) { + this.classLoaders.add(cl); + } + } + } + } + + /** + * @param parent The parent classloaders + * @param loaders A list of classloaders to be used to load classes or resources + */ + public ClassLoaderDelegate(ClassLoader parent, Collection<ClassLoader> loaders) { + super(parent); + if (loaders != null) { + for (ClassLoader cl : loaders) { + if (cl != null && cl != parent && !classLoaders.contains(cl)) { + this.classLoaders.add(cl); + } + } + } + } + + @Override + protected Class<?> findClass(String className) throws ClassNotFoundException { + for (ClassLoader parent : classLoaders) { + try { + return parent.loadClass(className); + } catch (ClassNotFoundException e) { + continue; + } + } + throw new ClassNotFoundException(className); + } + + @Override + protected URL findResource(String resName) { + for (ClassLoader parent : classLoaders) { + URL url = parent.getResource(resName); + if (url != null) { + return url; + } + } + return null; + } + + @SuppressWarnings("unchecked") + @Override + protected Enumeration<URL> findResources(String resName) throws IOException { + Enumeration<URL>[] enums = new Enumeration[classLoaders.size()]; + int index = 0; + for (ClassLoader parent : classLoaders) { + enums[index++] = parent.getResources(resName); + } + return new CompoundEnumeration<URL>(enums); + } +} diff --git a/sca-java-2.x/tags/2.0-M5.1-RC1/modules/common-java/src/main/java/org/apache/tuscany/sca/common/java/collection/CollectionMap.java b/sca-java-2.x/tags/2.0-M5.1-RC1/modules/common-java/src/main/java/org/apache/tuscany/sca/common/java/collection/CollectionMap.java new file mode 100644 index 0000000000..0db80c891d --- /dev/null +++ b/sca-java-2.x/tags/2.0-M5.1-RC1/modules/common-java/src/main/java/org/apache/tuscany/sca/common/java/collection/CollectionMap.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.tuscany.sca.common.java.collection; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.concurrent.ConcurrentHashMap; + +/** + * A Map with Collection values + */ +public class CollectionMap<K, V> extends ConcurrentHashMap<K, Collection<V>> { + private static final long serialVersionUID = -8926174610229029369L; + + public boolean putValue(K key, V value) { + Collection<V> collection = get(key); + if (collection == null) { + collection = createCollection(); + put(key, collection); + } + return collection.add(value); + } + + public boolean putValues(K key, Collection<? extends V> value) { + Collection<V> collection = get(key); + if (collection == null) { + collection = createCollection(); + put(key, collection); + } + return collection.addAll(value); + } + + public boolean removeValue(K key, V value) { + return removeValue(key, value, false); + } + + /** + * Remove an entry from the collection for a key + * @param key The key + * @param value The value in the collection + * @param removeEmptyEntry Indicate if the entry should be removed if the collection is empty + * @return + */ + public boolean removeValue(K key, V value, boolean removeEmptyEntry) { + Collection<V> collection = get(key); + if (collection == null) { + return false; + } + boolean result = collection.remove(value); + if(removeEmptyEntry && collection.isEmpty()) { + remove(key); + } + return result; + } + + protected Collection<V> createCollection() { + return new ArrayList<V>(); + } + +} diff --git a/sca-java-2.x/tags/2.0-M5.1-RC1/modules/common-java/src/main/java/org/apache/tuscany/sca/common/java/collection/CompoundEnumeration.java b/sca-java-2.x/tags/2.0-M5.1-RC1/modules/common-java/src/main/java/org/apache/tuscany/sca/common/java/collection/CompoundEnumeration.java new file mode 100644 index 0000000000..dc6a7e0f90 --- /dev/null +++ b/sca-java-2.x/tags/2.0-M5.1-RC1/modules/common-java/src/main/java/org/apache/tuscany/sca/common/java/collection/CompoundEnumeration.java @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.tuscany.sca.common.java.collection; + +import java.util.Enumeration; +import java.util.NoSuchElementException; + +public class CompoundEnumeration<T> implements Enumeration<T> { + private Enumeration<T>[] enumerations = null; + private int index = 0; + + public CompoundEnumeration(Enumeration<T>... enums) { + enumerations = enums; + } + + public boolean hasMoreElements() { + // if the current enum is null that means this enum is finished + if (currentEnumeration() == null) { + // No next enum + return false; + } + // If the current enum has more elements, lets go + return currentEnumeration().hasMoreElements(); + } + + private Enumeration<T> findNextEnumeration(boolean moveCursor) { + return findNextEnumeration(index, moveCursor); + } + + private Enumeration<T> findNextEnumeration(int cursor, boolean moveCursor) { + // next place in the array + int next = cursor + 1; + // If the cursor is still in the array + if (next < enumerations.length) { + + // If there is something in that place + // AND the enum is not empty + if (enumerations[next] != null && enumerations[next].hasMoreElements()) { + // OK + if (moveCursor) { + index = next; + } + return enumerations[next]; + } + // Try next element + return findNextEnumeration(next, moveCursor); + } + // No more elements available + return null; + } + + public T nextElement() { + // ask for the next element of the current enum. + if (currentEnumeration() != null) { + return currentEnumeration().nextElement(); + } + + // no more elements in this Enum + // We must throw a NoSuchElementException + throw new NoSuchElementException("No more elements"); + } + + private Enumeration<T> currentEnumeration() { + if (enumerations != null) { + if (index < enumerations.length) { + Enumeration<T> e = enumerations[index]; + if (e == null || !e.hasMoreElements()) { + // the current enum is null or empty + // we probably want to switch to the next one + e = findNextEnumeration(true); + } + return e; + } + } + return null; + } +} diff --git a/sca-java-2.x/tags/2.0-M5.1-RC1/modules/common-java/src/main/java/org/apache/tuscany/sca/common/java/collection/CompoundIterator.java b/sca-java-2.x/tags/2.0-M5.1-RC1/modules/common-java/src/main/java/org/apache/tuscany/sca/common/java/collection/CompoundIterator.java new file mode 100644 index 0000000000..997ba89881 --- /dev/null +++ b/sca-java-2.x/tags/2.0-M5.1-RC1/modules/common-java/src/main/java/org/apache/tuscany/sca/common/java/collection/CompoundIterator.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.tuscany.sca.common.java.collection; + +import java.util.Collection; +import java.util.Iterator; +import java.util.NoSuchElementException; + +public class CompoundIterator<T> implements Iterator<T> { + private Iterator<T>[] iterators = null; + private int index = 0; + + public CompoundIterator(Iterator<T>... iterators) { + this.iterators = iterators; + } + + @SuppressWarnings("unchecked") + public CompoundIterator(Collection<T>... collections) { + this.iterators = new Iterator[collections.length]; + for (int i = 0; i < collections.length; i++) { + this.iterators[i] = collections[i].iterator(); + } + } + + public boolean hasNext() { + // if the current enum is null that means this enum is finished + if (currentIterator() == null) { + // No next enum + return false; + } + // If the current enum has more elements, lets go + return currentIterator().hasNext(); + } + + private Iterator<T> findNextIterator(boolean moveCursor) { + return findNextIterator(index, moveCursor); + } + + private Iterator<T> findNextIterator(int cursor, boolean moveCursor) { + // next place in the array + int next = cursor + 1; + // If the cursor is still in the array + if (next < iterators.length) { + + // If there is something in that place + // AND the enum is not empty + if (iterators[next] != null && iterators[next].hasNext()) { + // OK + if (moveCursor) { + index = next; + } + return iterators[next]; + } + // Try next element + return findNextIterator(next, moveCursor); + } + // No more elements available + return null; + } + + public T next() { + // ask for the next element of the current enum. + if (currentIterator() != null) { + return currentIterator().next(); + } + + // no more elements in this Enum + // We must throw a NoSuchElementException + throw new NoSuchElementException("No more elements"); + } + + public void remove() { + // ask for the next element of the current enum. + if (currentIterator() != null) { + currentIterator().remove(); + } + + // no more elements in this Enum + // We must throw a NoSuchElementException + throw new NoSuchElementException("No more elements"); + } + + private Iterator<T> currentIterator() { + if (iterators != null) { + if (index < iterators.length) { + Iterator<T> e = iterators[index]; + if (e == null || !e.hasNext()) { + // the current enum is null or empty + // we probably want to switch to the next one + e = findNextIterator(true); + } + return e; + } + } + return null; + } +} diff --git a/sca-java-2.x/tags/2.0-M5.1-RC1/modules/common-java/src/main/java/org/apache/tuscany/sca/common/java/collection/LRUCache.java b/sca-java-2.x/tags/2.0-M5.1-RC1/modules/common-java/src/main/java/org/apache/tuscany/sca/common/java/collection/LRUCache.java new file mode 100644 index 0000000000..41286aa0f9 --- /dev/null +++ b/sca-java-2.x/tags/2.0-M5.1-RC1/modules/common-java/src/main/java/org/apache/tuscany/sca/common/java/collection/LRUCache.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.tuscany.sca.common.java.collection; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * A Simple LRU Cache + * + * @version $Revision$ + * @param <K> + * @param <V> + * @tuscany.spi.extension.asclient + */ + +public class LRUCache<K, V> extends LinkedHashMap<K, V> { + private static final long serialVersionUID = -342098639681884413L; + protected int maxCacheSize = 4096; + + /** + * Default constructor for an LRU Cache The default capacity is 4096 + */ + public LRUCache() { + this(0, 4096, 0.75f, true); + } + + /** + * Constructs a LRUCache with a maximum capacity + * + * @param maximumCacheSize + */ + public LRUCache(int maximumCacheSize) { + this(0, maximumCacheSize, 0.75f, true); + } + + /** + * Constructs an empty <tt>LRUCache</tt> instance with the specified + * initial capacity, maximumCacheSize,load factor and ordering mode. + * + * @param initialCapacity the initial capacity. + * @param maximumCacheSize + * @param loadFactor the load factor. + * @param accessOrder the ordering mode - <tt>true</tt> for access-order, + * <tt>false</tt> for insertion-order. + * @throws IllegalArgumentException if the initial capacity is negative or + * the load factor is non-positive. + */ + + public LRUCache(int initialCapacity, int maximumCacheSize, float loadFactor, boolean accessOrder) { + super(initialCapacity, loadFactor, accessOrder); + this.maxCacheSize = maximumCacheSize; + } + + /** + * @return Returns the maxCacheSize. + */ + public int getMaxCacheSize() { + return maxCacheSize; + } + + /** + * @param maxCacheSize The maxCacheSize to set. + */ + public void setMaxCacheSize(int maxCacheSize) { + this.maxCacheSize = maxCacheSize; + } + + @Override + protected boolean removeEldestEntry(Map.Entry<K, V> eldest) { + return size() > maxCacheSize; + } +} diff --git a/sca-java-2.x/tags/2.0-M5.1-RC1/modules/common-java/src/main/java/org/apache/tuscany/sca/common/java/io/IOHelper.java b/sca-java-2.x/tags/2.0-M5.1-RC1/modules/common-java/src/main/java/org/apache/tuscany/sca/common/java/io/IOHelper.java new file mode 100644 index 0000000000..91aef7a65f --- /dev/null +++ b/sca-java-2.x/tags/2.0-M5.1-RC1/modules/common-java/src/main/java/org/apache/tuscany/sca/common/java/io/IOHelper.java @@ -0,0 +1,167 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.tuscany.sca.common.java.io; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.JarURLConnection; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLConnection; + +/** + * Helper class for I/O operations + * @tuscany.spi.extension.asclient + */ +public class IOHelper { + + public static InputStream openStream(URL url) throws IOException { + // Handle file:<relative path> which strictly speaking is not a valid file URL + File file = toFile(url); + if (file != null) { + return new FileInputStream(file); + } + URLConnection connection = url.openConnection(); + if (connection instanceof JarURLConnection) { + // See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5041014 + connection.setUseCaches(false); + } + InputStream is = connection.getInputStream(); + return is; + } + + public static void close(InputStream is) { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + } + + /** + * Escape the space in URL string + * @param uri + * @return + */ + public static URI createURI(String uri) { + if (uri == null) { + return null; + } + if (uri.indexOf('%') != -1) { + // Avoid double-escaping + return URI.create(uri); + } + int index = uri.indexOf(':'); + String scheme = null; + String ssp = uri; + if (index != -1) { + scheme = uri.substring(0, index); + ssp = uri.substring(index + 1); + } + try { + return new URI(scheme, ssp, null); + } catch (URISyntaxException e) { + throw new IllegalArgumentException(e); + } + } + + public static URI toURI(URL url) { + if (url == null) { + return null; + } + return createURI(url.toString()); + } + + public static URL normalize(URL url) { + // Make sure the trailing / is added to the file directory URL so that + // URLClassLoader can load classes from that + try { + File file = toFile(url); + if (file != null) { + return file.toURI().toURL(); + } else { + return toURI(url).toURL(); + } + } catch (MalformedURLException e) { + throw new IllegalArgumentException(e); + } + } + + /** + * Returns the File object representing the given URL. + * + * @param url + * @return + */ + public static File toFile(URL url) { + if (url == null || !url.getProtocol().equals("file")) { + return null; + } else { + String filename = url.getFile().replace('/', File.separatorChar); + int pos = 0; + while ((pos = filename.indexOf('%', pos)) >= 0) { + if (pos + 2 < filename.length()) { + String hexStr = filename.substring(pos + 1, pos + 3); + char ch = (char)Integer.parseInt(hexStr, 16); + filename = filename.substring(0, pos) + ch + filename.substring(pos + 3); + } + } + return new File(filename); + } + } + + /** + * Returns the location of the classpath entry, JAR, WAR etc. containing the given class. + * + * @param clazz + * @return + */ + public static URL codeLocation(Class<?> clazz) { + URL url = clazz.getProtectionDomain().getCodeSource().getLocation(); + if (url == null) { + url = clazz.getResource("/" + clazz.getName().replace('.', '/') + ".class"); + } + return url; + } + + /** + * Returns a location string as a URL + * @param location + * @return + */ + public static URL getLocationAsURL(String location) { + URI uri = IOHelper.createURI(location); + if (uri.getScheme() == null) { + uri = new File(location).toURI(); + } + try { + return uri.toURL(); + } catch (MalformedURLException e) { + throw new IllegalArgumentException(location, e); + } + } + +} diff --git a/sca-java-2.x/tags/2.0-M5.1-RC1/modules/common-java/src/main/java/org/apache/tuscany/sca/common/java/reflection/JavaIntrospectionHelper.java b/sca-java-2.x/tags/2.0-M5.1-RC1/modules/common-java/src/main/java/org/apache/tuscany/sca/common/java/reflection/JavaIntrospectionHelper.java new file mode 100644 index 0000000000..ae4f01c032 --- /dev/null +++ b/sca-java-2.x/tags/2.0-M5.1-RC1/modules/common-java/src/main/java/org/apache/tuscany/sca/common/java/reflection/JavaIntrospectionHelper.java @@ -0,0 +1,586 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.tuscany.sca.common.java.reflection; + +import java.beans.Introspector; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.lang.reflect.WildcardType; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Logger; + +import org.apache.tuscany.sca.core.ExtensionPointRegistry; +import org.apache.tuscany.sca.core.UtilityExtensionPoint; +import org.oasisopen.sca.ServiceReference; + +/** + * Implements various reflection-related operations + * + * @version $Rev$ $Date$ + */ +public final class JavaIntrospectionHelper { + private static final Logger logger = Logger.getLogger(JavaIntrospectionHelper.class.getName()); + private static final Class<?>[] EMPTY_CLASS_ARRY = new Class[0]; + + private ExtensionPointRegistry registry; + + public JavaIntrospectionHelper(ExtensionPointRegistry registry) { + this.registry = registry; + } + + public static JavaIntrospectionHelper getInstance(ExtensionPointRegistry registry) { + UtilityExtensionPoint utilities = registry.getExtensionPoint(UtilityExtensionPoint.class); + return utilities.getUtility(JavaIntrospectionHelper.class); + } + + private static boolean matches(int modifiers, int include, int exclude) { + if (include != -1) { + if ((modifiers & include) == 0) { + return false; + } + } + if (exclude != -1) { + if ((modifiers & exclude) != 0) { + return false; + } + } + return true; + } + + /** + * Recursively evaluates the type hierarchy to return all fields that are + * public or protected + * @param clazz The java class + * @param fields The set that collects all fields + * @param include The modifiers to be included + * @param exclude The modifiers to be excluded + * @return The set that collects all matched fields + */ + private static Set<Field> getAllFields(Class<?> clazz, Set<Field> fields, int include, int exclude) { + if (clazz == null || clazz.isArray() || Object.class.equals(clazz)) { + return fields; + } + fields = getAllFields(clazz.getSuperclass(), fields, include, exclude); + Field[] declaredFields = clazz.getDeclaredFields(); + for (final Field field : declaredFields) { + int modifiers = field.getModifiers(); + if (matches(modifiers, include, exclude)) { + // Allow privileged access to set accessibility. Requires ReflectPermission + // in security policy. + AccessController.doPrivileged(new PrivilegedAction<Object>() { + public Object run() { + field.setAccessible(true); // ignore Java accessibility + return null; + } + }); + fields.add(field); + } + } + return fields; + } + + /** + * @param cls + * @return + */ + public Set<Field> getAllNonStaticOrFinalFields(Class<?> cls) { + Set<Field> fields = new HashSet<Field>(); + return getAllFields(cls, fields, -1, Modifier.STATIC | Modifier.FINAL); + } + + /** + * @param cls + * @return + */ + public Set<Method> getAllNonStaticMethods(Class<?> cls) { + Set<Method> methods = new HashSet<Method>(); + return getAllUniqueMethods(cls, methods, -1, Modifier.STATIC); + } + + /** + * Recursively evaluates the type hierarchy to return all unique methods + */ + private static Set<Method> getAllUniqueMethods(Class<?> pClass, Set<Method> methods, int include, int exclude) { + if (pClass == null || pClass.isArray() || Object.class.equals(pClass)) { + return methods; + } + // we first evaluate methods of the subclass and then move to the parent + Method[] declaredMethods = pClass.getDeclaredMethods(); + for (final Method declaredMethod : declaredMethods) { + int modifiers = declaredMethod.getModifiers(); + if (!matches(modifiers, include, exclude)) { + continue; + } + if (methods.size() == 0) { + methods.add(declaredMethod); + } else { + List<Method> temp = new ArrayList<Method>(); + boolean matched = false; + for (Method method : methods) { + // only add if not already in the set from a superclass (i.e. + // the method is not overridden) + if (exactMethodMatch(declaredMethod, method)) { + matched = true; + break; + } + } + if (!matched) { + // Allow privileged access to set accessibility. Requires ReflectPermission + // in security policy. + AccessController.doPrivileged(new PrivilegedAction<Object>() { + public Object run() { + declaredMethod.setAccessible(true); + return null; + } + }); + temp.add(declaredMethod); + } + methods.addAll(temp); + temp.clear(); + } + } + // evaluate class hierarchy - this is done last to track inherited + // methods + methods = getAllUniqueMethods(pClass.getSuperclass(), methods, include, exclude); + return methods; + } + + /** + * Finds the closest matching field with the given name, that is, a field of + * the exact specified type or, alternately, of a supertype. + * + * @param name the name of the field + * @param type the field type + * @param fields the collection of fields to search + * @return the matching field or null if not found + */ + public static Field findClosestMatchingField(String name, Class type, Set<Field> fields) { + Field candidate = null; + for (Field field : fields) { + if (field.getName().equals(name)) { + if (field.getType().equals(type)) { + return field; // exact match + } else if (field.getType().isAssignableFrom(type) || (field.getType().isPrimitive() && primitiveAssignable(field + .getType(), + type))) { + // We could have the situation where a field parameter is a + // primitive and the demarshalled value is + // an object counterpart (e.g. Integer and int) + // @spec issue + // either an interface or super class, so keep a reference + // until + // we know there are no closer types + candidate = field; + } + } + } + if (candidate != null) { + return candidate; + } else { + return null; + } + } + + /** + * Finds the closest matching method with the given name, that is, a method + * taking the exact parameter types or, alternately, parameter supertypes. + * + * @param name the name of the method + * @param types the method parameter types + * @param methods the collection of methods to search + * @return the matching method or null if not found + */ + public static Method findClosestMatchingMethod(String name, Class[] types, Set<Method> methods) { + if (types == null) { + types = EMPTY_CLASS_ARRY; + } + Method candidate = null; + for (Method method : methods) { + if (method.getName().equals(name) && method.getParameterTypes().length == types.length) { + Class<?>[] params = method.getParameterTypes(); + boolean disqualify = false; + boolean exactMatch = true; + for (int i = 0; i < params.length; i++) { + if (!params[i].equals(types[i]) && !params[i].isAssignableFrom(types[i])) { + // no match + disqualify = true; + exactMatch = false; + break; + } else if (!params[i].equals(types[i]) && params[i].isAssignableFrom(types[i])) { + // not exact match + exactMatch = false; + } + } + if (disqualify) { + continue; + } else if (exactMatch) { + return method; + } else { + candidate = method; + } + } + } + if (candidate != null) { + return candidate; + } else { + return null; + } + } + + /** + * Determines if two methods "match" - that is, they have the same method + * names and exact parameter types (one is not a supertype of the other) + */ + public static boolean exactMethodMatch(Method method1, Method method2) { + if (!method1.getName().equals(method2.getName())) { + return false; + } + Class<?>[] types1 = method1.getParameterTypes(); + Class<?>[] types2 = method2.getParameterTypes(); + if (types1.length != types2.length) { + return false; + } + boolean matched = true; + for (int i = 0; i < types1.length; i++) { + if (types1[i] != types2[i]) { + matched = false; + break; + } + } + return matched; + } + + public static <T> Constructor<T> getDefaultConstructor(Class<T> clazz) throws NoSuchMethodException { + return clazz.getConstructor(); + } + + /** + * Returns the simple name of a class - i.e. the class name devoid of its + * package qualifier + * + * @param implClass the implementation class + */ + public static String getBaseName(Class<?> implClass) { + return implClass.getSimpleName(); + } + + public static boolean isImmutable(Class<?> clazz) { + return String.class == clazz || clazz.isPrimitive() + || Number.class.isAssignableFrom(clazz) + || Boolean.class.isAssignableFrom(clazz) + || Character.class.isAssignableFrom(clazz) + || Byte.class.isAssignableFrom(clazz); + } + + /** + * Takes a property name and converts it to a getter method name according + * to JavaBean conventions. For example, property + * <code>foo<code> is returned as <code>getFoo</code> + */ + public static String toGetter(String name) { + return "get" + name.toUpperCase().substring(0, 1) + name.substring(1); + } + + /** + * Takes a setter or getter method name and converts it to a property name + * according to JavaBean conventions. For example, <code>setFoo(var)</code> + * is returned as property <code>foo<code> + */ + public static String toPropertyName(Method method) { + if (isGetter(method) || isSetter(method)) { + String name = method.getName(); + if (name.startsWith("get") || name.startsWith("set")) { + return Introspector.decapitalize(name.substring(3)); + } else { + // Starts with is + return Introspector.decapitalize(name.substring(2)); + } + } else { + throw new IllegalArgumentException("Method is not a setter or getter: " + method); + } + } + + public static Class<?> getErasure(Type type) { + if (type instanceof Class) { + return (Class<?>)type; + } else if (type instanceof GenericArrayType) { + // FIXME: How to deal with the []? + GenericArrayType arrayType = (GenericArrayType)type; + return getErasure(arrayType.getGenericComponentType()); + } else if (type instanceof ParameterizedType) { + ParameterizedType pType = (ParameterizedType)type; + return getErasure(pType.getRawType()); + } else if (type instanceof WildcardType) { + WildcardType wType = (WildcardType)type; + Type[] types = wType.getUpperBounds(); + return getErasure(types[0]); + } else if (type instanceof TypeVariable) { + TypeVariable<?> var = (TypeVariable<?>)type; + Type[] types = var.getBounds(); + return getErasure(types[0]); + } + return null; + } + + public static Class<?> getBaseType(Class<?> cls, Type genericType) { + if (cls.isArray()) { + return cls.getComponentType(); + } else if (Collection.class.isAssignableFrom(cls)) { + if (genericType instanceof ParameterizedType) { + // Collection<BaseType> + ParameterizedType parameterizedType = (ParameterizedType)genericType; + Type baseType = parameterizedType.getActualTypeArguments()[0]; + if (baseType instanceof GenericArrayType) { + // Base is array + return cls; + } else { + return getErasure(baseType); + } + } else { + return cls; + } + } else { + return cls; + } + } + + public static Type getParameterType(Type type) { + if (type instanceof ParameterizedType) { + // Collection<BaseType> + ParameterizedType parameterizedType = (ParameterizedType)type; + Type baseType = parameterizedType.getActualTypeArguments()[0]; + return baseType; + } else { + return Object.class; + } + } + + public static Class<?> getBusinessInterface(Class<?> cls, Type callableReferenceType) { + if (ServiceReference.class.isAssignableFrom(cls) && callableReferenceType instanceof ParameterizedType) { + // Collection<BaseType> + ParameterizedType parameterizedType = (ParameterizedType)callableReferenceType; + Type baseType = parameterizedType.getActualTypeArguments()[0]; + if (baseType instanceof GenericArrayType) { + // Base is array + return cls; + } else { + return getErasure(baseType); + } + } + return Object.class; + } + + /** + * Takes a property name and converts it to a setter method name according + * to JavaBean conventions. For example, the property + * <code>foo<code> is returned as <code>setFoo(var)</code> + */ + public static String toSetter(String name) { + return "set" + name.toUpperCase().substring(0, 1) + name.substring(1); + } + + /** + * Compares a two types, assuming one is a primitive, to determine if the + * other is its object counterpart + */ + private static boolean primitiveAssignable(Class<?> memberType, Class<?> param) { + if (memberType == Integer.class) { + return param == Integer.TYPE; + } else if (memberType == Double.class) { + return param == Double.TYPE; + } else if (memberType == Float.class) { + return param == Float.TYPE; + } else if (memberType == Short.class) { + return param == Short.TYPE; + } else if (memberType == Character.class) { + return param == Character.TYPE; + } else if (memberType == Boolean.class) { + return param == Boolean.TYPE; + } else if (memberType == Byte.class) { + return param == Byte.TYPE; + } else if (param == Integer.class) { + return memberType == Integer.TYPE; + } else if (param == Double.class) { + return memberType == Double.TYPE; + } else if (param == Float.class) { + return memberType == Float.TYPE; + } else if (param == Short.class) { + return memberType == Short.TYPE; + } else if (param == Character.class) { + return memberType == Character.TYPE; + } else if (param == Boolean.class) { + return memberType == Boolean.TYPE; + } else if (param == Byte.class) { + return memberType == Byte.TYPE; + } else { + return false; + } + } + + /** + * Returns the generic types represented in the given type. Usage as + * follows: <code> + * JavaIntrospectionHelper.getGenerics(field.getGenericType()); + * <p/> + * JavaIntrospectionHelper.getGenerics(m.getGenericParameterTypes()[0];); </code> + * + * @return the generic types in order of declaration or an empty array if + * the type is not genericized + */ + public static List<? extends Type> getGenerics(Type genericType) { + List<Type> classes = new ArrayList<Type>(); + if (genericType instanceof ParameterizedType) { + ParameterizedType ptype = (ParameterizedType)genericType; + // get the type arguments + Type[] targs = ptype.getActualTypeArguments(); + for (Type targ : targs) { + classes.add(targ); + } + } + return classes; + } + + /** + * Returns the generic type specified by the class at the given position as + * in: <p/> <code> public class Foo<Bar,Baz>{ //.. } + * <p/> + * JavaIntrospectionHelper.introspectGeneric(Foo.class,1); <code> + * <p/> + * will return Baz. + */ + public static Class introspectGeneric(Class<?> clazz, int pos) { + assert clazz != null : "No class specified"; + Type type = clazz.getGenericSuperclass(); + if (type instanceof ParameterizedType) { + Type[] args = ((ParameterizedType)type).getActualTypeArguments(); + if (args.length <= pos) { + throw new IllegalArgumentException("Invalid index value for generic class " + clazz.getName()); + } + return (Class)((ParameterizedType)type).getActualTypeArguments()[pos]; + } else { + Type[] interfaces = clazz.getGenericInterfaces(); + for (Type itype : interfaces) { + if (!(itype instanceof ParameterizedType)) { + continue; + } + ParameterizedType interfaceType = (ParameterizedType)itype; + return (Class)interfaceType.getActualTypeArguments()[0]; + } + } + return null; + } + + /** + * Returns the set of interfaces implemented by the given class and its + * ancestors or a blank set if none + */ + public Set<Class<?>> getAllInterfaces(Class<?> clazz) { + Set<Class<?>> implemented = new HashSet<Class<?>>(); + getAllInterfaces(clazz, implemented); + return implemented; + } + + private static void getAllInterfaces(Class<?> clazz, Set<Class<?>> implemented) { + Class<?>[] interfaces = clazz.getInterfaces(); + for (Class<?> interfaze : interfaces) { + implemented.add(interfaze); + } + Class<?> superClass = clazz.getSuperclass(); + // Object has no superclass so check for null + if (superClass != null && !superClass.equals(Object.class)) { + getAllInterfaces(superClass, implemented); + } + } + + /** + * Test if a method follows the JavaBeans setter pattern + * @param method + * @return + */ + public static boolean isSetter(Method method) { + return (void.class == method.getReturnType() && method.getParameterTypes().length == 1 + && method.getName().startsWith("set") && method.getName().length() > 3); + } + + /** + * Test if a method follows the JavaBeans getter pattern + * @param method + * @return + */ + public static boolean isGetter(Method method) { + if (method.getParameterTypes().length == 0) { + return false; + } + Class<?> returnType = method.getReturnType(); + if (returnType == void.class || returnType == Void.class) { + return false; + } + String name = method.getName(); + if (returnType == boolean.class) + if (name.length() > 2 && name.startsWith("is")) { + return true; + } + return name.length() > 3 && name.startsWith("get"); + } + + private final static Map<Class<?>, String> signatures = new HashMap<Class<?>, String>(); + static { + signatures.put(boolean.class, "Z"); + signatures.put(byte.class, "B"); + signatures.put(char.class, "C"); + signatures.put(short.class, "S"); + signatures.put(int.class, "I"); + signatures.put(long.class, "J"); + signatures.put(float.class, "F"); + signatures.put(double.class, "D"); + }; + + public static String getSignature(Class<?> cls) { + if (cls.isPrimitive()) { + return signatures.get(cls); + } + if (cls.isArray()) { + return "[" + getSignature(cls.getComponentType()); + } + return "L" + cls.getName().replace('.', '/') + ";"; + } + + public static Class<?> getArrayType(Class<?> componentType, int dims) throws ClassNotFoundException { + StringBuffer buf = new StringBuffer(); + for (int i = 0; i < dims; i++) { + buf.append('['); + } + buf.append(getSignature(componentType)); + return Class.forName(buf.toString(), false, componentType.getClassLoader()); + } +} |