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    }