/* Copyright (C) 2004 - 2006 db4objects Inc. http://www.db4o.com
This file is part of the db4o open source object database.
db4o is free software; you can redistribute it and/or modify it under
the terms of version 2 of the GNU General Public License as published
by the Free Software Foundation and as clarified by db4objects' GPL
interpretation policy, available at
http://www.db4o.com/about/company/legalpolicies/gplinterpretation/
Alternatively you can write to db4objects, Inc., 1900 S Norfolk Street,
Suite 350, San Mateo, CA 94403, USA.
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, write to the Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
package com.db4o.internal;
import java.io.IOException;
import java.net.Socket;
import java.util.Date;
import com.db4o.Db4o;
import com.db4o.Debug;
import com.db4o.Deploy;
import com.db4o.ObjectContainer;
import com.db4o.P1Collection;
import com.db4o.config.Configuration;
import com.db4o.config.ObjectAttribute;
import com.db4o.config.ObjectClass;
import com.db4o.config.TClass;
import com.db4o.config.THashtable;
import com.db4o.config.TNull;
import com.db4o.config.TVector;
import com.db4o.foundation.Collection4;
import com.db4o.foundation.TernaryBool;
import com.db4o.foundation.Visitor4;
import com.db4o.internal.handlers.ArrayHandler;
import com.db4o.internal.handlers.MultidimensionalArrayHandler;
import com.db4o.internal.handlers.NetTypeHandler;
import com.db4o.internal.handlers.net.NetDateTime;
import com.db4o.internal.handlers.net.NetDecimal;
import com.db4o.internal.handlers.net.NetSByte;
import com.db4o.internal.handlers.net.NetUInt;
import com.db4o.internal.handlers.net.NetULong;
import com.db4o.internal.handlers.net.NetUShort;
import com.db4o.internal.query.processor.QConEvaluation;
import com.db4o.query.Candidate;
import com.db4o.query.Evaluation;
import com.db4o.reflect.ReflectClass;
import com.db4o.reflect.Reflector;
import com.db4o.reflect.generic.GenericReflector;
import com.db4o.types.Db4oCollections;
/**
* @exclude
* @sharpen.ignore
*/
public final class Platform4 {
private static final String JDK_PACKAGE = "com.db4o.internal.";
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 final String DB4O_ASSEMBLY = ", db4o";
// 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
};
static final void addShutDownHook(Object a_stream, Object a_lock) {
synchronized (a_lock) {
if (hasShutDownHook()) {
if (shutDownThread == null) {
shutDownRunnable = new ShutDownRunnable();
shutDownThread = jdk().addShutdownHook(shutDownRunnable);
}
shutDownRunnable.ensure(a_stream);
}
}
}
public static final boolean canSetAccessible() {
if (Deploy.csharp) {
return true;
}
if (setAccessibleCheck.unspecified()) {
if (Deploy.csharp) {
setAccessibleCheck = TernaryBool.NO;
} else {
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) {
try {
return Class.forName(className) != null;
} catch (Throwable t) {
return false;
}
}
static Db4oCollections collections(Object a_object){
return jdk().collections((ObjectContainerBase)a_object);
}
static final Reflector createReflector(Object classLoader){
return jdk().createReflector(classLoader);
}
static final Object createReferenceQueue() {
return jdk().createReferenceQueue();
}
public static Object createWeakReference(Object obj){
return jdk().createWeakReference(obj);
}
static final Object createYapRef(Object a_queue, Object a_yapObject, Object a_object) {
return jdk().createYapRef(a_queue, (ObjectReference) a_yapObject, a_object);
}
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()) {
Object[] objects;
if (claxx.getComponentType().isArray()) {
objects = new MultidimensionalArrayHandler(stream, null, false).allElements(obj);
} else {
objects = new ArrayHandler(stream, null, false).allElements(obj);
}
for (int i = 0; i < objects.length; i++) {
flattenCollection1(stream, objects[i], col);
}
} else {
flattenCollection2(stream, obj, col);
}
}
}
static final void flattenCollection2(final ObjectContainerBase a_stream, Object a_object, final com.db4o.foundation.Collection4 col) {
Reflector reflector = a_stream.reflector();
if (reflector.forObject(a_object).isCollection()) {
forEachCollectionElement(a_object, new Visitor4() {
public void visit(Object obj) {
flattenCollection1(a_stream, obj, col);
}
});
} else {
col.add(a_object);
}
}
static final void forEachCollectionElement(Object a_object, Visitor4 a_visitor) {
jdk().forEachCollectionElement(a_object, a_visitor);
}
public static final String format(Date date, boolean showTime) {
return jdk().format(date, showTime);
}
public static Object getClassForType(Object obj) {
return obj;
}
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));
}
config.objectClass("java.lang.StringBuffer").compare(new ObjectAttribute() {
public Object attribute(Object original) {
if (original instanceof StringBuffer) {
return ((StringBuffer) original).toString();
}
return original;
}
});
translate(config.objectClass("java.lang.Class"), "TClass");
translateCollection(config, "Hashtable", "THashtable", true);
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", true);
translateUtilNull(config, "HashMap");
translateUtilNull(config, "WeakHashMap");
translate(config, UTIL + "TreeMap", "TTreeMap");
} catch (Exception e) {
}
} else {
translateCollection(config, "Vector", "TVector", false);
}
netReadAsJava(config, "ext.Db4oDatabase");
netReadAsJava(config, "P1Object");
netReadAsJava(config, "P1Collection");
netReadAsJava(config, "P1HashElement");
netReadAsJava(config, "P1ListElement");
netReadAsJava(config, "P2HashMap");
netReadAsJava(config, "P2LinkedList");
netReadAsJava(config, "StaticClass");
netReadAsJava(config, "StaticField");
}
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.unspecified()) {
if (!Deploy.csharp) {
if (classIsAvailable(UTIL + "Collection")) {
collectionCheck = TernaryBool.YES;
return true;
}
}
collectionCheck = TernaryBool.NO;
}
return collectionCheck.definiteYes();
}
public static final boolean hasLockFileThread() {
return !Deploy.csharp;
}
public static final boolean hasNio() {
if (!Debug.nio) {
return false;
}
if (nioCheck.unspecified()) {
if ((jdk().ver() >= 4)
&& (!noNIO())) {
nioCheck = TernaryBool.YES;
return true;
}
nioCheck = TernaryBool.NO;
}
return nioCheck.definiteYes();
}
static final boolean hasShutDownHook() {
if (shutDownHookCheck.unspecified()) {
if (!Deploy.csharp) {
if (jdk().ver() >= 3){
shutDownHookCheck = TernaryBool.YES;
return true;
}
JDKReflect.invoke(System.class, RUNFINALIZERSONEXIT, new Class[] {boolean.class}, new Object[]{new Boolean(true)});
}
shutDownHookCheck = TernaryBool.NO;
}
return shutDownHookCheck.definiteYes();
}
static final boolean hasWeakReferences() {
if (!Debug.weakReferences) {
return false;
}
if (weakReferenceCheck.unspecified()) {
if (!Deploy.csharp) {
if (classIsAvailable(ACCESSIBLEOBJECT)
&& classIsAvailable(REFERENCEQUEUE)
&& jdk().ver() >= 2) {
weakReferenceCheck = TernaryBool.YES;
return true;
}
}
weakReferenceCheck = TernaryBool.NO;
}
return weakReferenceCheck.definiteYes();
}
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);
}
public static final boolean isValueType(ReflectClass claxx){
return false;
}
public static JDK jdk() {
if (jdkWrapper == null) {
createJdk();
}
return jdkWrapper;
}
private static void createJdk() {
if (classIsAvailable("java.lang.reflect.Method")){
jdkWrapper = (JDK)createInstance(JDK_PACKAGE + "JDKReflect");
}
if (classIsAvailable(Platform4.ACCESSIBLEOBJECT)){
jdkWrapper = createJDKWrapper("1_2");
}
if (jdk().methodIsAvailable("java.lang.Runtime","addShutdownHook",
new Class[] { Thread.class })){
jdkWrapper = createJDKWrapper("1_3");
}
if(classIsAvailable("java.nio.channels.FileLock")){
jdkWrapper = createJDKWrapper("1_4");
}
if(classIsAvailable("java.lang.Enum")){
jdkWrapper = createJDKWrapper("5");
}
}
private static JDK createJDKWrapper(String name){
JDK newWrapper = (JDK)createInstance(JDK_PACKAGE + "JDK_" + name);
if(newWrapper != null){
return newWrapper;
}
return jdkWrapper;
}
/**
* use for system classes only, since not ClassLoader
* or Reflector-aware
*/
private static final Object createInstance(String name) {
try {
Class clazz = Class.forName(name);
if(clazz != null){
return clazz.newInstance();
}
} catch (Exception e) {
}
return null;
}
public static boolean isSimple(Class a_class){
for (int i = 0; i < SIMPLE_CLASSES.length; i++) {
if(a_class == SIMPLE_CLASSES[i]){
return true;
}
}
return false;
}
static final void killYapRef(Object a_object){
jdk().killYapRef(a_object);
}
public static void link(){
if (!Deploy.csharp) {
// link standard translators, so they won't get deleted
// by deployment
new TClass();
new TVector();
new THashtable();
new TNull();
}
}
// FIXME: functionality should really be in IoAdapter
public static final void lockFile(String path,Object file) throws IOException {
if (!hasNio()) {
return;
}
// FIXME: libgcj 3.x isn't able to properly lock the database file
String fullversion = System.getProperty("java.fullversion");
if (fullversion != null && fullversion.indexOf("GNU libgcj") >= 0) {
System.err.println("Warning: Running in libgcj 3.x--not locking database file!");
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);
}
static void markTransient(String a_marker) {
// do nothing
}
static boolean callConstructor() {
if (callConstructorCheck.unspecified()) {
if(jdk().methodIsAvailable(
REFLECTIONFACTORY,
GETCONSTRUCTOR,
new Class[]{Class.class, jdk().constructorClass()}
)){
callConstructorCheck = TernaryBool.NO;
return false;
}
callConstructorCheck = TernaryBool.YES;
}
return callConstructorCheck.definiteYes();
}
private static final void netReadAsJava(Config4Impl config, String className){
Config4Class classConfig = (Config4Class)config.objectClass(DB4O_PACKAGE + className + DB4O_ASSEMBLY);
if(classConfig == null){
return;
}
classConfig.maintainMetaClass(false);
classConfig.readAs(DB4O_PACKAGE + className);
}
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;
}
}
static final void pollReferenceQueue(Object a_stream, Object a_referenceQueue) {
jdk().pollReferenceQueue((ObjectContainerBase) a_stream, a_referenceQueue);
}
public static void postOpen(ObjectContainer a_oc) {
// do nothing
}
static void preClose(ObjectContainer a_oc) {
// do nothing
}
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) {
if(!Deploy.csharp){
reflector.registerCollection(P1Collection.class);
jdk().registerCollections(reflector);
}
}
static final void removeShutDownHook(Object a_stream, Object a_lock) {
synchronized (a_lock) {
if (hasShutDownHook()) {
if (shutDownRunnable != null) {
shutDownRunnable.remove(a_stream);
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 (!Deploy.csharp) {
if (setAccessibleCheck == TernaryBool.UNSPECIFIED) {
canSetAccessible();
}
if (setAccessibleCheck == TernaryBool.YES) {
jdk().setAccessible(a_accessible);
}
}
}
public static boolean storeStaticFieldValues(Reflector reflector, ReflectClass claxx) {
return jdk().isEnum(reflector, claxx);
}
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);
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(ObjectContainerBase stream) {
return new NetTypeHandler[] {
new NetDateTime(stream),
new NetDecimal(stream),
new NetSByte(stream),
new NetUInt(stream),
new NetULong(stream),
new NetUShort(stream)
};
}
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) {
throw Exceptions4.virtualException();
}
public static boolean isTransient(ReflectClass a_class) {
return false;
}
public static boolean isDb4oClass(String className) {
if (className.indexOf("com.db4o.test") == 0) {
return false;
}
return className.indexOf("com.db4o") == 0;
}
public static String fullyQualifiedName(Class clazz) {
return clazz.getName();
}
}