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