/* This file is part of the db4o object database http://www.db4o.com Copyright (C) 2004 - 2011 Versant Corporation http://www.versant.com db4o is free software; you can redistribute it and/or modify it under the terms of version 3 of the GNU General Public License as published by the Free Software Foundation. db4o is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. */ package com.db4o.internal; import java.io.*; import java.net.*; import java.util.*; import com.db4o.*; import com.db4o.config.*; import com.db4o.ext.*; import com.db4o.foundation.*; import com.db4o.internal.activation.*; import com.db4o.internal.handlers.*; import com.db4o.internal.handlers.array.*; import com.db4o.internal.query.processor.*; import com.db4o.io.*; import com.db4o.query.*; import com.db4o.reflect.*; import com.db4o.reflect.generic.*; /** * @exclude * @sharpen.ignore */ public final class Platform4 { static private TernaryBool collectionCheck=TernaryBool.UNSPECIFIED; static private JDK jdkWrapper; static private TernaryBool nioCheck=TernaryBool.UNSPECIFIED; static private TernaryBool setAccessibleCheck=TernaryBool.UNSPECIFIED; static private TernaryBool shutDownHookCheck=TernaryBool.UNSPECIFIED; static TernaryBool callConstructorCheck=TernaryBool.UNSPECIFIED; static ShutDownRunnable shutDownRunnable; static Thread shutDownThread; static final String ACCESSIBLEOBJECT = "java.lang.reflect.AccessibleObject"; static final String GETCONSTRUCTOR = "newConstructorForSerialization"; static final String REFERENCEQUEUE = "java.lang.ref.ReferenceQueue"; static final String REFLECTIONFACTORY = "sun.reflect.ReflectionFactory"; static final String RUNFINALIZERSONEXIT = "runFinalizersOnExit"; static final String UTIL = "java.util."; static final String DB4O_PACKAGE = "com.db4o."; static final String DB4O_CONFIG = DB4O_PACKAGE + "config."; // static private int cCreateNewFile; static private TernaryBool weakReferenceCheck=TernaryBool.UNSPECIFIED; private static final Class[] SIMPLE_CLASSES = { Integer.class, Long.class, Float.class, Boolean.class, Double.class, Byte.class, Character.class, Short.class, String.class, java.util.Date.class }; synchronized static final void addShutDownHook(ObjectContainerBase container) { if (!hasShutDownHook()) { return; } if (shutDownThread == null) { shutDownRunnable = new ShutDownRunnable(); shutDownThread = jdk().addShutdownHook(shutDownRunnable); } shutDownRunnable.ensure(container); } public static final boolean canSetAccessible() { if (setAccessibleCheck.isUnspecified()) { if (jdk().ver() >= 2) { setAccessibleCheck = TernaryBool.YES; } else { setAccessibleCheck = TernaryBool.NO; if (((Config4Impl)Db4o.configure()).messageLevel() >= 0) { Messages.logErr(Db4o.configure(), 47, null, null); } } } return setAccessibleCheck.definiteYes(); } /** * use for system classes only, since not ClassLoader * or Reflector-aware */ static final boolean classIsAvailable(String className) { return ReflectPlatform.forName(className) != null; } static final Reflector createReflector(Object classLoader){ return jdk().createReflector(classLoader); } public static final Object createReferenceQueue() { return jdk().createReferenceQueue(); } public static Object createWeakReference(Object obj){ return jdk().createWeakReference(obj); } public static final Object createActiveObjectReference(Object referenceQueue, Object objectReference, Object obj) { return jdk().createActivateObjectReference(referenceQueue, (ObjectReference) objectReference, obj); } public static Object deserialize(byte[] bytes) { return jdk().deserialize(bytes); } public static final long doubleToLong(double a_double) { return Double.doubleToLongBits(a_double); } public static final QConEvaluation evaluationCreate(Transaction a_trans, Object example){ if(example instanceof Evaluation){ return new QConEvaluation(a_trans, example); } return null; } public static final void evaluationEvaluate(Object a_evaluation, Candidate a_candidate){ ((Evaluation)a_evaluation).evaluate(a_candidate); } /** may be needed for YapConfig processID() at a later date */ /* static boolean createNewFile(File file) throws IOException{ return file.createNewFile(); } */ public static Object[] collectionToArray(ObjectContainerBase stream, Object obj){ Collection4 col = flattenCollection(stream, obj); Object[] ret = new Object[col.size()]; col.toArray(ret); return ret; } static final Collection4 flattenCollection(ObjectContainerBase stream, Object obj) { Collection4 col = new Collection4(); flattenCollection1(stream, obj, col); return col; } /** * Should create additional configuration, for example through reflection * on annotations. * * - If a valid configuration is passed as classConfig, any additional * configuration, if available, should be applied to this object, and * this object should be returned. * - If classConfig is null and there is no additional configuration, * null should be returned. * - If classConfig is null and there is additional configuration, this code * should create and register a new configuration via config.objectClass(), * apply additional configuration there and return this new instance. * * The reason for this dispatch is to avoid creation of a configuration * for a class that doesn't need configuration at all. * * @param clazz The class to be searched for additional configuration information * @param config The global database configuration * @param classConfig A class configuration, if one already exists * @return classConfig, if not null, a newly created ObjectClass otherwise. */ public static Config4Class extendConfiguration(ReflectClass clazz,Configuration config,Config4Class classConfig) { return jdk().extendConfiguration(clazz, config, classConfig); } static final void flattenCollection1(ObjectContainerBase stream, Object obj, Collection4 col) { if (obj == null) { col.add(null); } else { ReflectClass claxx = stream.reflector().forObject(obj); if (claxx.isArray()) { Iterator4 objects = ArrayHandler.iterator(claxx, obj); while (objects.moveNext()) { flattenCollection1(stream, objects.current(), col); } } else { flattenCollection2(stream, obj, col); } } } static final void flattenCollection2(final ObjectContainerBase container, Object obj, final Collection4 col) { if (container.reflector().forObject(obj).isCollection()) { forEachCollectionElement(obj, new Visitor4() { public void visit(Object element) { flattenCollection1(container, element, col); } }); } else { col.add(obj); } } public static final void forEachCollectionElement(Object obj, Visitor4 visitor) { jdk().forEachCollectionElement(obj, visitor); } public static final String format(Date date, boolean showTime) { return jdk().format(date, showTime); } public static final void getDefaultConfiguration(Config4Impl config) { // Initialize all JDK stuff first, before doing ClassLoader stuff jdk(); hasWeakReferences(); hasNio(); hasCollections(); hasShutDownHook(); if(config.reflector()==null) { config.reflectWith(jdk().createReflector(null)); } configStringBufferCompare(config); translate(config.objectClass("java.lang.Class"), "TClass"); translateCollection(config, "Hashtable", "THashtable", false); if (jdk().ver() >= 2) { try { translateCollection(config, "AbstractCollection", "TCollection", false); translateUtilNull(config, "AbstractList"); translateUtilNull(config, "AbstractSequentialList"); translateUtilNull(config, "LinkedList"); translateUtilNull(config, "ArrayList"); translateUtilNull(config, "Vector"); translateUtilNull(config, "Stack"); translateUtilNull(config, "AbstractSet"); translateUtilNull(config, "HashSet"); translate(config, UTIL + "TreeSet", "TTreeSet"); translateCollection(config, "AbstractMap", "TMap", false); translateUtilNull(config, "HashMap"); translateUtilNull(config, "WeakHashMap"); translate(config, UTIL + "TreeMap", "TTreeMap"); } catch (Exception e) { } } else { translateCollection(config, "Vector", "TVector", false); } config.objectClass(ActivatableBase.class).indexed(false); jdk().commonConfigurations(config); jdk().extendConfiguration(config); } /** * @deprecated uses deprecated API */ private static void configStringBufferCompare(Config4Impl config) { config.objectClass("java.lang.StringBuffer").compare(new ObjectAttribute() { public Object attribute(Object original) { if (original instanceof StringBuffer) { return ((StringBuffer) original).toString(); } return original; } }); } public static Object getTypeForClass(Object obj){ return obj; } static final Object getYapRefObject(Object a_object) { return jdk().getYapRefObject(a_object); } static final synchronized boolean hasCollections() { if (collectionCheck.isUnspecified()) { if (classIsAvailable(UTIL + "Collection")) { collectionCheck = TernaryBool.YES; return true; } collectionCheck = TernaryBool.NO; } return collectionCheck.definiteYes(); } public static boolean needsLockFileThread(){ return ! hasNio(); } private static final boolean hasNio() { if (!Debug4.nio) { return false; } if (nioCheck.isUnspecified()) { if ((jdk().ver() >= 4) && (!noNIO())) { nioCheck = TernaryBool.YES; return true; } nioCheck = TernaryBool.NO; } return nioCheck.definiteYes(); } static final boolean hasShutDownHook() { if (shutDownHookCheck.isUnspecified()) { if (jdk().ver() >= 3){ shutDownHookCheck = TernaryBool.YES; return true; } Reflection4.invoke(System.class, RUNFINALIZERSONEXIT, new Class[] {boolean.class}, new Object[]{new Boolean(true)}); shutDownHookCheck = TernaryBool.NO; } return shutDownHookCheck.definiteYes(); } public static final boolean hasWeakReferences() { if (!Debug4.weakReferences) { return false; } if (weakReferenceCheck.isUnspecified()) { if (classIsAvailable(ACCESSIBLEOBJECT) && classIsAvailable(REFERENCEQUEUE) && jdk().ver() >= 2) { weakReferenceCheck = TernaryBool.YES; return true; } weakReferenceCheck = TernaryBool.NO; } return weakReferenceCheck.definiteYes(); } /** @param obj */ static final boolean ignoreAsConstraint(Object obj){ return false; } static final boolean isCollectionTranslator(Config4Class a_config) { return jdk().isCollectionTranslator(a_config); } public static boolean isConnected(Socket socket) { return jdk().isConnected(socket); } /** * Returns true if claxx represents a .net struct (a value type with * members in the type handler jargon). * * @param claxx **/ public static final boolean isStruct(ReflectClass claxx){ return false; } public static JDK jdk() { if (jdkWrapper == null) { createJdk(); } return jdkWrapper; } public static class JDKFactoryInstantiationException extends RuntimeException { public JDKFactoryInstantiationException(Throwable cause) { super(JDKFactory.class.getName() + " instances must have a public default constructor and be accessible from " + Platform4.class.getName() + " ("+cause.getMessage()+")"); } } private static void createJdk() { Class<?>[] jdkFactories = { JDK_5.Factory.class,//1 switched both positions. DalvikVM.Factory.class,//2 JDK_1_4.Factory.class, JDK_1_3.Factory.class, JDK_1_2.Factory.class, JDKReflect.Factory.class, }; for (Class<?> clazz : jdkFactories) { try { jdkWrapper = ((JDKFactory) clazz.newInstance()).tryToCreate(); if (jdkWrapper != null) { break; } } catch (SecurityException e) { throw new JDKFactoryInstantiationException(e); } catch (IllegalAccessException e) { throw new JDKFactoryInstantiationException(e); } catch (InstantiationException e) { throw new JDKFactoryInstantiationException(e); } } } public static boolean isSimple(Class clazz){ for (int i = 0; i < SIMPLE_CLASSES.length; i++) { if(clazz == SIMPLE_CLASSES[i]){ return true; } } return false; } static final void killYapRef(Object a_object){ jdk().killYapRef(a_object); } public static void link(){ // link standard translators, so they won't get deleted // by deployment new TClass(); new TVector(); new THashtable(); new TNull(); } public static final void lockFile(String path,Object file) { if (!hasNio()) { return; } jdk().lockFile(path,file); } public static final void unlockFile(String path,Object file) { if (hasNio()) { jdk().unlockFile(path,file); } } public static final double longToDouble(long a_long) { return Double.longBitsToDouble(a_long); } /** @param marker */ static void markTransient(String marker) { // do nothing } public static boolean callConstructor() { if (callConstructorCheck.isUnspecified()) { if(jdk().supportSkipConstructorCall()){ callConstructorCheck = TernaryBool.NO; return false; } callConstructorCheck = TernaryBool.YES; } return callConstructorCheck.definiteYes(); } private static final boolean noNIO() { try { if (propertyIs("java.vendor", "Sun") && propertyIs("java.version", "1.4.0") && (propertyIs("os.name", "Linux") || propertyIs("os.name", "Windows 95") || propertyIs("os.name", "Windows 98"))) { return true; } return false; } catch (Exception e) { return true; } } public static final void pollReferenceQueue(Object a_stream, Object a_referenceQueue) { jdk().pollReferenceQueue((ObjectContainerBase) a_stream, a_referenceQueue); } private static final boolean propertyIs(String propertyName, String propertyValue) { String property = System.getProperty(propertyName); return (property != null) && (property.indexOf(propertyValue) == 0); } public static void registerCollections(GenericReflector reflector) { jdk().registerCollections(reflector); } synchronized static final void removeShutDownHook(ObjectContainerBase container) { if (!hasShutDownHook() || shutDownRunnable == null) { return; } shutDownRunnable.remove(container); if (shutDownRunnable.size() == 0) { if (!shutDownRunnable.dontRemove) { try { jdk().removeShutdownHook(shutDownThread); } catch (Exception e) { // this is safer than attempting perfect // synchronisation } } shutDownThread = null; shutDownRunnable = null; } } public static final byte[] serialize(Object obj) throws Exception{ return jdk().serialize(obj); } public static final void setAccessible(Object a_accessible) { if (setAccessibleCheck == TernaryBool.UNSPECIFIED) { canSetAccessible(); } if (setAccessibleCheck == TernaryBool.YES) { jdk().setAccessible(a_accessible); } } public static boolean storeStaticFieldValues(Reflector reflector, ReflectClass claxx) { return isEnum(reflector, claxx); } public static boolean isEnum(Reflector reflector, ReflectClass claxx) { return jdk().isEnum(reflector, claxx); } public static boolean isJavaEnum(GenericReflector reflector, ReflectClass classReflector) { return isEnum(reflector, classReflector); } private static final void translate(ObjectClass oc, String to) { ((Config4Class)oc).translateOnDemand(DB4O_CONFIG + to); } private static final void translate(Config4Impl config, String from, String to) { translate(config.objectClass(from), to); } private static final void translateCollection( Config4Impl config, String from, String to, boolean cascadeOnDelete) { ObjectClass oc = config.objectClass(UTIL + from); // FIXME: Maybe we don't need any special // configuration here. Or should this be 2 // after the Typehandler changes ? oc.updateDepth(3); if (cascadeOnDelete) { oc.cascadeOnDelete(true); } translate(oc, to); } private static final void translateUtilNull(Config4Impl config, String className) { translate(config, UTIL + className, "TNull"); } static final NetTypeHandler[] types(Reflector reflector) { return jdk().types(reflector); } public static byte[] updateClassName(byte[] bytes) { // needed for .NET only: update assembly names if necessary return bytes; } public static Object weakReferenceTarget(Object weakRef){ return jdk().weakReferenceTarget(weakRef); } public static Object wrapEvaluation(Object evaluation) { return evaluation; } /** @param claxx */ public static boolean isTransient(ReflectClass claxx) { return false; } public static Reflector reflectorForType(Class clazz) { return jdk().reflectorForType(clazz); } public static Date now(){ return new Date(); } public static boolean useNativeSerialization() { return jdk().useNativeSerialization(); } public static void registerPlatformHandlers(ObjectContainerBase container) { container.handlers().treatAsOpenType(java.lang.Number.class); } public static Class nullableTypeFor(Class primitiveJavaClass) { if(_primitive2Wrapper == null) initPrimitive2Wrapper(); Class wrapperClazz = (Class)_primitive2Wrapper.get(primitiveJavaClass); if(wrapperClazz==null) throw new NotImplementedException("No nullableTypeFor : " + primitiveJavaClass.getName()); return wrapperClazz; } private static void initPrimitive2Wrapper(){ _primitive2Wrapper = new Hashtable4(); _primitive2Wrapper.put(int.class, Integer.class); _primitive2Wrapper.put(byte.class, Byte.class); _primitive2Wrapper.put(short.class, Short.class); _primitive2Wrapper.put(float.class, Float.class); _primitive2Wrapper.put(double.class, Double.class); _primitive2Wrapper.put(long.class, Long.class); _primitive2Wrapper.put(boolean.class, Boolean.class); _primitive2Wrapper.put(char.class, Character.class); } private static Hashtable4 _primitive2Wrapper; public static Object nullValue(Class clazz) { if(_nullValues == null) { initNullValues(); } return _nullValues.get(clazz); } private static void initNullValues() { _nullValues = new Hashtable4(); _nullValues.put(boolean.class, Boolean.FALSE); _nullValues.put(byte.class, new Byte((byte)0)); _nullValues.put(short.class, new Short((short)0)); _nullValues.put(char.class, new Character((char)0)); _nullValues.put(int.class, new Integer(0)); _nullValues.put(float.class, new Float(0.0)); _nullValues.put(long.class, new Long(0)); _nullValues.put(double.class, new Double(0.0)); } private static Hashtable4 _nullValues; public static Class[] primitiveTypes() { return new Class[] { boolean.class, byte.class, short.class, char.class, int.class, long.class, float.class, double.class, }; } public static void throwUncheckedException(Throwable origExc) { if(origExc instanceof RuntimeException){ throw (RuntimeException)origExc; } if(origExc instanceof Error){ throw (Error)origExc; } jdk().throwIllegalArgumentException(origExc); } public static final byte toSByte(byte b){ return b; } public static Storage newStorage() { return new FileStorage(); } public static String asUtf8(byte[] byteArray) { try { return new String(byteArray, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new Db4oIllegalStateException(e); } } public static void printStackTrace(Throwable t, PrintWriter out) { t.printStackTrace(out); } }