/*
* ModeShape (http://www.modeshape.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.modeshape.common.util;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
import org.modeshape.common.annotation.Immutable;
/**
* Static utilities for working with classes.
*/
@Immutable
public final class ClassUtil {
private static void addObjectString( Object object,
int includeInheritedFieldDepth,
Class<?> clazz,
StringBuilder text ) {
// Add class's name
text.append(nonPackageQualifiedName(clazz));
text.append('(');
// Add class's field names and object's corresponding values
Field[] flds = clazz.getDeclaredFields();
boolean separatorNeeded = false;
for (int ndx = 0, len = flds.length; ndx < len; ++ndx) {
Field fld = flds[ndx];
try {
// Attempt to ensure fields is accessible. Getting the value will throw an exception if the attempt failed.
makeAccessible(fld);
Object val = fld.get(object);
// Skip static fields
if ((fld.getModifiers() & Modifier.STATIC) != 0) {
continue;
}
// Skip synthetic fields
String name = fld.getName();
if (name.indexOf('$') >= 0) {
continue;
}
// Add separator in text between fields
separatorNeeded = addSeparator(separatorNeeded, text);
// Add field's name and value to text
text.append(fld.getName());
text.append('=');
text.append(val);
} catch (Exception err) {
}
}
// Add inheritied fields if requested
if (includeInheritedFieldDepth > 0) {
separatorNeeded = addSeparator(separatorNeeded, text);
addObjectString(object, includeInheritedFieldDepth - 1, clazz.getSuperclass(), text);
}
text.append(')');
}
private static boolean addSeparator( boolean separatorNeeded,
StringBuilder text ) {
if (separatorNeeded) {
text.append(", ");
}
return true;
}
public static void makeAccessible( final AccessibleObject object ) {
if (!object.isAccessible()) {
if (System.getSecurityManager() == null) {
object.setAccessible(true);
} else {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
object.setAccessible(true);
return null;
}
});
}
}
}
/**
* @param clazz A class.
* @return The non-package-qualified name of the specified class. Note, inner class names will still be qualified by their
* enclosing class names and a "$" delimiter.
*/
public static String nonPackageQualifiedName( final Class<?> clazz ) {
// if (clazz == null) {
// throw new IllegalArgumentException(I18n.format(CommonI18n.mustNotBeNull, "Class"));
// }
String name = clazz.getName();
return name.substring(name.lastIndexOf('.') + 1);
}
/**
* @param object An object.
* @return The non-package-qualified name of the class of the specified object. Note, inner class names will still be
* qualified by their enclosing class names and a "$" delimiter.
*/
public static String nonPackageQualifiedName( final Object object ) {
// if (object == null) {
// throw new IllegalArgumentException(I18n.format(CommonI18n.mustNotBeNull, "Object"));
// }
return nonPackageQualifiedName(object.getClass());
}
/**
* @param object
* @param includeInheritedFieldDepth
* @return A string representation of the specified object, consisting of its class name, properties, and property values.
*/
public static String toString( Object object,
int includeInheritedFieldDepth ) {
StringBuilder text = new StringBuilder();
addObjectString(object, includeInheritedFieldDepth, object.getClass(), text);
return text.toString();
}
/**
* Determine whether the supplied string represents a well-formed fully-qualified Java classname. This utility method enforces
* no conventions (e.g., packages are all lowercase) nor checks whether the class is available on the classpath.
*
* @param classname
* @return true if the string is a fully-qualified class name
*/
public static boolean isFullyQualifiedClassname( String classname ) {
if (classname == null) return false;
String[] parts = classname.split("[\\.]");
if (parts.length == 0) return false;
for (String part : parts) {
CharacterIterator iter = new StringCharacterIterator(part);
// Check first character (there should at least be one character for each part) ...
char c = iter.first();
if (c == CharacterIterator.DONE) return false;
if (!Character.isJavaIdentifierStart(c) && !Character.isIdentifierIgnorable(c)) return false;
c = iter.next();
// Check the remaining characters, if there are any ...
while (c != CharacterIterator.DONE) {
if (!Character.isJavaIdentifierPart(c) && !Character.isIdentifierIgnorable(c)) return false;
c = iter.next();
}
}
return true;
}
private ClassUtil() {
}
}