1 /*
2 * ModeShape (http://www.modeshape.org)
3 * See the COPYRIGHT.txt file distributed with this work for information
4 * regarding copyright ownership. Some portions may be licensed
5 * to Red Hat, Inc. under one or more contributor license agreements.
6 * See the AUTHORS.txt file in the distribution for a full listing of
7 * individual contributors.
8 *
9 * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
10 * is licensed to you under the terms of the GNU Lesser General Public License as
11 * published by the Free Software Foundation; either version 2.1 of
12 * the License, or (at your option) any later version.
13 *
14 * ModeShape is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this software; if not, write to the Free
21 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23 */
24 package org.modeshape.common.util;
25
26 import java.lang.reflect.AccessibleObject;
27 import java.lang.reflect.Field;
28 import java.lang.reflect.Modifier;
29 import java.security.AccessController;
30 import java.security.PrivilegedAction;
31 import java.text.CharacterIterator;
32 import java.text.StringCharacterIterator;
33 import net.jcip.annotations.Immutable;
34
35 /**
36 * Static utilities for working with classes.
37 */
38 @Immutable
39 public final class ClassUtil {
40
41 private static void addObjectString( Object object,
42 int includeInheritedFieldDepth,
43 Class<?> clazz,
44 StringBuffer text ) {
45
46 // Add class's name
47 text.append(nonPackageQualifiedName(clazz));
48
49 text.append('(');
50
51 // Add class's field names and object's corresponding values
52 Field[] flds = clazz.getDeclaredFields();
53 boolean separatorNeeded = false;
54 for (int ndx = 0, len = flds.length; ndx < len; ++ndx) {
55 Field fld = flds[ndx];
56 try {
57
58 // Attempt to ensure fields is accessible. Getting the value will throw an exception if the attempt failed.
59 makeAccessible(fld);
60 Object val = fld.get(object);
61
62 // Skip static fields
63 if ((fld.getModifiers() & Modifier.STATIC) != 0) {
64 continue;
65 }
66
67 // Skip synthetic fields
68 String name = fld.getName();
69 if (name.indexOf('$') >= 0) {
70 continue;
71 }
72
73 // Add separator in text between fields
74 separatorNeeded = addSeparator(separatorNeeded, text);
75
76 // Add field's name and value to text
77 text.append(fld.getName());
78 text.append('=');
79 text.append(val);
80
81 } catch (Exception err) {
82 }
83 }
84
85 // Add inheritied fields if requested
86 if (includeInheritedFieldDepth > 0) {
87 separatorNeeded = addSeparator(separatorNeeded, text);
88 addObjectString(object, includeInheritedFieldDepth - 1, clazz.getSuperclass(), text);
89 }
90
91 text.append(')');
92 }
93
94 private static boolean addSeparator( boolean separatorNeeded,
95 StringBuffer text ) {
96 if (separatorNeeded) {
97 text.append(", ");
98 }
99 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"));
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"));
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 }