View Javadoc

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 }