001 /*
002 * JBoss DNA (http://www.jboss.org/dna)
003 * See the COPYRIGHT.txt file distributed with this work for information
004 * regarding copyright ownership. Some portions may be licensed
005 * to Red Hat, Inc. under one or more contributor license agreements.
006 * See the AUTHORS.txt file in the distribution for a full listing of
007 * individual contributors.
008 *
009 * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
010 * is licensed to you under the terms of the GNU Lesser General Public License as
011 * published by the Free Software Foundation; either version 2.1 of
012 * the License, or (at your option) any later version.
013 *
014 * JBoss DNA is distributed in the hope that it will be useful,
015 * but WITHOUT ANY WARRANTY; without even the implied warranty of
016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017 * Lesser General Public License for more details.
018 *
019 * You should have received a copy of the GNU Lesser General Public
020 * License along with this software; if not, write to the Free
021 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
022 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
023 */
024 package org.jboss.dna.common.util;
025
026 import java.lang.reflect.AccessibleObject;
027 import java.lang.reflect.Field;
028 import java.lang.reflect.Modifier;
029 import java.security.AccessController;
030 import java.security.PrivilegedAction;
031 import java.text.CharacterIterator;
032 import java.text.StringCharacterIterator;
033
034 /**
035 * Static utilities for working with classes.
036 *
037 * @author John Verhaeg
038 */
039 public final class ClassUtil {
040
041 private static void addObjectString( Object object,
042 int includeInheritedFieldDepth,
043 Class<?> clazz,
044 StringBuffer text ) {
045
046 // Add class's name
047 text.append(nonPackageQualifiedName(clazz));
048
049 text.append('(');
050
051 // Add class's field names and object's corresponding values
052 Field[] flds = clazz.getDeclaredFields();
053 boolean separatorNeeded = false;
054 for (int ndx = 0, len = flds.length; ndx < len; ++ndx) {
055 Field fld = flds[ndx];
056 try {
057
058 // Attempt to ensure fields is accessible. Getting the value will throw an exception if the attempt failed.
059 makeAccessible(fld);
060 Object val = fld.get(object);
061
062 // Skip static fields
063 if ((fld.getModifiers() & Modifier.STATIC) != 0) {
064 continue;
065 }
066
067 // Skip synthetic fields
068 String name = fld.getName();
069 if (name.indexOf('$') >= 0) {
070 continue;
071 }
072
073 // Add separator in text between fields
074 separatorNeeded = addSeparator(separatorNeeded, text);
075
076 // Add field's name and value to text
077 text.append(fld.getName());
078 text.append('=');
079 text.append(val);
080
081 } catch (Exception err) {
082 }
083 }
084
085 // Add inheritied fields if requested
086 if (includeInheritedFieldDepth > 0) {
087 separatorNeeded = addSeparator(separatorNeeded, text);
088 addObjectString(object, includeInheritedFieldDepth - 1, clazz.getSuperclass(), text);
089 }
090
091 text.append(')');
092 }
093
094 private static boolean addSeparator( boolean separatorNeeded,
095 StringBuffer text ) {
096 if (separatorNeeded) {
097 text.append(", "); //$NON-NLS-1$
098 }
099 return true;
100 }
101
102 /**
103 * @param object
104 */
105 public static void makeAccessible( final AccessibleObject object ) {
106 if (!object.isAccessible()) {
107 if (System.getSecurityManager() == null) {
108 object.setAccessible(true);
109 } else {
110 AccessController.doPrivileged(new PrivilegedAction<Object>() {
111
112 public Object run() {
113 object.setAccessible(true);
114 return null;
115 }
116 });
117 }
118 }
119 }
120
121 /**
122 * @param clazz A class.
123 * @return The non-package-qualified name of the specified class. Note, inner class names will still be qualified by their
124 * enclosing class names and a "$" delimiter.
125 */
126 public static String nonPackageQualifiedName( final Class<?> clazz ) {
127 // if (clazz == null) {
128 // throw new IllegalArgumentException(I18n.format(CommonI18n.mustNotBeNull, "Class")); //$NON-NLS-1$
129 // }
130 String name = clazz.getName();
131 return name.substring(name.lastIndexOf('.') + 1);
132 }
133
134 /**
135 * @param object An object.
136 * @return The non-package-qualified name of the class of the specified object. Note, inner class names will still be
137 * qualified by their enclosing class names and a "$" delimiter.
138 */
139 public static String nonPackageQualifiedName( final Object object ) {
140 // if (object == null) {
141 // throw new IllegalArgumentException(I18n.format(CommonI18n.mustNotBeNull, "Object")); //$NON-NLS-1$
142 // }
143 return nonPackageQualifiedName(object.getClass());
144 }
145
146 /**
147 * @param object
148 * @param includeInheritedFieldDepth
149 * @return A string representation of the specified object, consisting of its class name, properties, and property values.
150 */
151 public static String toString( Object object,
152 int includeInheritedFieldDepth ) {
153 StringBuffer text = new StringBuffer();
154 addObjectString(object, includeInheritedFieldDepth, object.getClass(), text);
155 return text.toString();
156 }
157
158 /**
159 * Determine whether the supplied string represents a well-formed fully-qualified Java classname. This utility method enforces
160 * no conventions (e.g., packages are all lowercase) nor checks whether the class is available on the classpath.
161 *
162 * @param classname
163 * @return true if the string is a fully-qualified class name
164 */
165 public static boolean isFullyQualifiedClassname( String classname ) {
166 if (classname == null) return false;
167 String[] parts = classname.split("[\\.]");
168 if (parts.length == 0) return false;
169 for (String part : parts) {
170 CharacterIterator iter = new StringCharacterIterator(part);
171 // Check first character (there should at least be one character for each part) ...
172 char c = iter.first();
173 if (c == CharacterIterator.DONE) return false;
174 if (!Character.isJavaIdentifierStart(c) && !Character.isIdentifierIgnorable(c)) return false;
175 c = iter.next();
176 // Check the remaining characters, if there are any ...
177 while (c != CharacterIterator.DONE) {
178 if (!Character.isJavaIdentifierPart(c) && !Character.isIdentifierIgnorable(c)) return false;
179 c = iter.next();
180 }
181 }
182 return true;
183 }
184
185 private ClassUtil() {
186 }
187 }