org.w3c.spi2.Structures Maven / Gradle / Ivy
/*
* ====================================================================
* Project: openMDX/Core, http://www.openmdx.org/
* Description: Structures
* Owner: OMEX AG, Switzerland, http://www.omex.ch
* ====================================================================
*
* This software is published under the BSD license as listed below.
*
* Copyright (c) 2004-2013, OMEX AG, Switzerland
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of the openMDX team nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* ------------------
*
* This product includes software developed by other organizations as
* listed in the NOTICE file.
*/
package org.w3c.spi2;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.AbstractList;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.SortedMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.jdo.PersistenceManager;
import javax.jdo.spi.PersistenceCapable;
import javax.jmi.reflect.RefStruct;
import javax.resource.cci.IndexedRecord;
import javax.resource.cci.MappedRecord;
import javax.resource.cci.Record;
import org.omg.mof.spi.Identifier;
import org.omg.mof.spi.Names;
import org.openmdx.base.accessor.jmi.cci.RefStruct_1_0;
import org.openmdx.base.collection.Maps;
import org.openmdx.base.resource.Records;
import org.openmdx.kernel.collection.ArraysExtension;
import org.openmdx.kernel.collection.TreeSparseArray;
import org.openmdx.kernel.jdo.ReducedJDOHelper;
import org.openmdx.kernel.loading.Classes;
import org.w3c.cci2.SortedMaps;
import org.w3c.cci2.SparseArray;
/**
* Structures
*/
public class Structures {
/**
* Constructor
*/
protected Structures() {
// Avoid instantiation
}
/**
* Create a structure proxy instance
*
* @param persistenceManager
* @param structureClass the structure's interface
* @param values the structure's values
*
* @return an initialized structure proxy instance
*
* @throws IllegalArgumentException
*/
private static S create(
PersistenceManager persistenceManager,
Class structureClass,
MetaData metaData, Object... values
) {
return Classes.newProxyInstance(
new ProxyHandler(persistenceManager, metaData, values),
structureClass, PersistenceAware.class
);
}
/**
* Create a structure proxy instance
*
* @param structureClass
* the structure's interface
* @param values
* the structure's values
*
* @return an initialized structure proxy instance
*
* @throws IllegalArgumentException
*/
public static S create(
Class structureClass,
Object... values
) {
MetaData metaData = MetaData.getInstance(structureClass);
return create(
null, // persistenceManager
structureClass,
metaData,
values
);
}
/**
* Create a structure proxy instance
*
* @param structureClass
* the structure's interface
* @param members
* the structure's members
*
* @return an initialized structure proxy instance
*
* @throws IllegalArgumentException
*/
public static S create(
Class structureClass,
Structures.Member>... members
) {
MetaData metaData = MetaData.getInstance(structureClass);
return create(
null, // persistenceManager
structureClass,
metaData,
metaData.toValues(members)
);
}
/**
* Create a structure proxy instance
*
* @param structureClass
* the structure's interface
* @param members
* the structure's members
*
* @return an initialized structure proxy instance
*
* @throws IllegalArgumentException
*/
public static S create(
Class structureClass,
List extends Structures.Member>> members
) {
MetaData metaData = MetaData.getInstance(structureClass);
return create(
null, // persistenceManager
structureClass,
metaData,
metaData.toValues(members)
);
}
/**
* Create a structure from a map
*
* @param persistenceManager the persistence manager is optional
* if the record does not contain references to persistence capable
* objects.
* @param structureClass
* @param content
*
* @return the corresponding structure proxy
*
* @throws IllegalArgumentException
*/
public static S create(
PersistenceManager persistenceManager,
Class structureClass,
Map,?> content
){
MetaData metaData = MetaData.getInstance(structureClass);
return create(
persistenceManager,
structureClass,
metaData,
metaData.toValues(persistenceManager, content)
);
}
/**
* Create a structure from a map
*
* @param persistenceManager the persistence manager is optional
* if the record does not contain references to persistence capable
* objects.
* @param structureClass
* @param content
*
* @return the corresponding structure proxy
*
* @throws IllegalArgumentException
*/
public static Object create(
PersistenceManager persistenceManager,
List structureClass,
Map,?> content
){
try {
return create(
persistenceManager,
Classes.getApplicationClass(cciClassName(structureClass)),
content
);
} catch (ClassNotFoundException exception) {
throw newIllegalArgumentException(
"structure",
structureClass,
exception
);
}
}
/**
* Create a structure from a Java Bean
*
* @param persistenceManager the persistence manager is optional
* if the record does not contain references to persistence capable
* objects.
* @param structureClass
* @param javaBean
*
* @return the corresponding structure proxy
*
* @throws IllegalArgumentException
*/
public static S fromJavaBean(
PersistenceManager persistenceManager,
Class structureClass,
Object javaBean
){
if(javaBean == null) {
return null;
} else {
MetaData metaData = MetaData.getInstance(structureClass);
return create(
persistenceManager,
structureClass,
metaData,
metaData.fromJavaBean(persistenceManager, javaBean)
);
}
}
/**
* Save a record into a Java Bean
*
* @param source the record to be saved
* @param target the target bean
*
* @exception IllegalArgumentException if the record can't be saved into the bean
* @exception NullPointerException if either the source or the target is null
*/
public static T toJavaBean(
MappedRecord source,
T target
){
try {
return MetaData.getInstance(source).toJavaBean(source, target);
} catch (Exception exception) {
throw new IllegalArgumentException(
"Unable to save the record type " + source.getRecordName() + " into the java bean type " + target.getClass().getName(),
exception
);
}
}
/**
* Save a structure into a Java Bean
*
* @param source the structure to be saved
* @param target the target bean
*
* @exception IllegalArgumentException if the record can't be saved into the bean
* @exception NullPointerException if either the source or the target is null
*/
public static T toJavaBean(
Object source,
T target
){
return toJavaBean(
source instanceof MappedRecord ? (MappedRecord)source : toRecord(source, true, true),
target
);
}
/**
* Retrieve the record type
*
* @param record
*
* @return the record name components
*/
static List refTypeName(
Record record
){
String name = record.getRecordName();
return Arrays.asList(name.split(name.indexOf("::") > 0 ? "::" : ":"));
}
/**
* Create a structure from a JCA record
*
* @param persistenceManager the persistence manager is optional
* if the record does not contain references to persistence capable
* objects.
*
* @param record
*
* @return the corresponding structure proxy
*
* @throws IllegalArgumentException
*/
public static Object create(
PersistenceManager persistenceManager,
MappedRecord record
){
return create(
persistenceManager,
refTypeName(record),
record
);
}
/**
* Return a mapped record view of the given structure
*
* @param structure
* @param mapNullValues if true, fields with value==null are added
* to the mapped record. They are not added if mapNullValues
* is false.
*
* @return a mapped record view of the given structure
*
* @exception ClassCastException unless isStructureInstance(structure) evaluates to true
*/
public static MappedRecord toRecord(
Object structure,
boolean mapNullValues
){
return toRecord(structure, mapNullValues, true);
}
/**
* Return a mapped record view of the given structure
*
* @param structure
* @param mapNullValues if true, fields with value==null are added to the mapped record.
* They are not added if mapNullValues is false.
* @param mofCompliant when true use "::"
as separator, ":"
otherwise
*
* @return a mapped record view of the given structure
*
* @exception ClassCastException unless isStructureInstance(structure) evaluates to true
*/
public static MappedRecord toRecord(
Object structure,
boolean mapNullValues,
boolean mofCompliant
){
return ((PersistenceAware)structure).openmdxjdoRecord(mapNullValues, mofCompliant);
}
/**
* Tests whether an object is a structure instance
*
* @param object
* the object to be tested
*
* @return true
if the object is a structure instance
*/
public static boolean isStructureInstance(Object object) {
return object != null && Proxy.isProxyClass(object.getClass())
&& Proxy.getInvocationHandler(object) instanceof ProxyHandler;
}
static IllegalArgumentException newIllegalArgumentException(
String type,
List qualifiedClassName,
ClassNotFoundException cause
){
StringBuilder message = new StringBuilder(
"Can't find interface for "
).append(
type
);
String separator = " ";
for(String element : qualifiedClassName) {
message.append(separator).append(element);
separator = "::";
}
return new IllegalArgumentException(
message.toString(),
cause
);
}
/**
* Convert a model class name to the corresponding cci class name
*
* @param modelClass a model class
*
* @return the corresponding cci class name
*/
static String cciClassName (
List modelClass
){
StringBuilder javaClass = new StringBuilder();
int iLimit = modelClass.size() - 1;
for(
int i = 0;
i < iLimit;
i++
){
javaClass.append(
Identifier.PACKAGE_NAME.toIdentifier(modelClass.get(i))
).append(
'.'
);
}
return javaClass.append(
Names.CCI2_PACKAGE_SUFFIX
).append(
'.'
).append(
Identifier.CLASS_PROXY_NAME.toIdentifier(modelClass.get(iLimit))
).toString(
);
}
//------------------------------------------------------------------------
// Class ProxyHandler
//------------------------------------------------------------------------
/**
* ProxyHandler
*/
private static class ProxyHandler
implements InvocationHandler, Serializable
{
/**
* Constructor
*
* @param persistenceManager the (optional) PersistenceManager
* @param values
* @param nameClass
*/
ProxyHandler(
PersistenceManager persistenceManager,
MetaData metaData,
Object... values
) {
this.metaData = metaData;
this.values = toNestedArrays(values);
}
/**
* Implements Serializable
*/
private static final long serialVersionUID = 8952038983241186303L;
private final Object[] values;
private transient PersistenceManager persistenceManager;
private transient Iterable>[] collections;
// private transient Iterable>[] collections;
private transient Class> structureClass;
private transient MetaData metaData;
private transient int hashCode = 0;
private transient String string = null;
/**
* Record names are MOF compliant qualified names using "::" as separator
*/
private transient MappedRecord record = null;
/**
* Record names are internal qualified names using ":" as separator
*/
private transient MappedRecord delegate = null;
/**
* Collection modification exception
*/
private static final String UNMODIFIABLE = "Structure members are unmodifiable";
/**
*
*/
private static final Object[] EMPTY_SPARSE_ARRAY = new Object[] {
new int[] {}, new Object[] {}
};
/**
*
*/
private static final Object[] EMPTY_COLLECTION = new Object[] {
};
/**
*
*/
private static final Integer[] NO_INDICES = new Integer[] {
};
/*
* (non-Javadoc)
*
* @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object,
* java.lang.reflect.Method, java.lang.Object[])
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Class> declaringClass = method.getDeclaringClass();
String methodName = method.getName();
if (this.structureClass == null) {
this.structureClass = proxy.getClass().getInterfaces()[0];
}
if (this.metaData == null) {
this.metaData = MetaData.getInstance(structureClass);
}
if (Object.class == declaringClass) {
if ("equals".equals(methodName)) {
if (this == args[0]) {
return Boolean.TRUE;
} else if (!getClass().isInstance(args[0])) {
return Boolean.FALSE;
} else {
ProxyHandler that = (ProxyHandler) Proxy.getInvocationHandler(args[0]);
return Boolean.valueOf(Arrays.deepEquals(this.values, that.values));
}
} else if ("toString".equals(methodName)) {
if (this.string == null) {
this.string = toRecord(true, true).toString();
}
return this.string;
} else if ("hashCode".equals(methodName)) {
if (this.hashCode == 0) {
this.hashCode = Arrays.deepHashCode(this.values);
}
return Integer.valueOf(this.hashCode);
}
} else if (PersistenceAware.class == declaringClass) {
if ("openmdxjdoValues".equals(methodName)) {
Object[] values = new Object[this.metaData.names.length];
for(Enum> member : this.metaData.names) {
values[member.ordinal()] = get(member);
}
return values;
} else if ("openmdxjdoRecord".equals(methodName)) {
if(this.record == null) {
this.record = toRecord(Boolean.TRUE.equals(args[0]), Boolean.TRUE.equals(args[1]));
}
return this.record;
}
} else if(RefStruct_1_0.class.equals(declaringClass)) {
if("refDelegate".equals(methodName)) {
if(this.delegate == null) {
this.delegate = toRecord(true, false);
}
return this.delegate;
}
} else {
Enum> member = this.metaData.accessors.get(method);
if (member != null) return get(member);
}
throw new UnsupportedOperationException(declaringClass.getName()
+ '.' + methodName + " is not supported by "
+ ProxyHandler.class.getName());
}
/**
* Retrieve a given field
*
* @param field
*
* @return
*/
private Object get(
Enum> field
){
int slot = field.ordinal();
Object v = this.values[slot];
Class> t = this.metaData.memberTypes[slot];
if (Iterable.class.isAssignableFrom(t)) {
if (this.collections == null) {
this.collections = new Iterable>[this.values.length];
} else if (this.collections[slot] != null) {
return this.collections[slot];
}
if (List.class == t) {
return this.collections[slot] = v == null ?
Collections.EMPTY_LIST :
new UnmodifiableList((Object[]) v);
} else if (Set.class == t) {
return this.collections[slot] = v == null ?
Collections.EMPTY_SET :
new UnmodifiableSet((Object[]) v);
} else if (SparseArray.class == t) {
Object[] iv = v == null ?
EMPTY_SPARSE_ARRAY :
(Object[]) v;
return this.collections[slot] = SortedMaps.asSparseArray(
new UnmodifiableSortedMap(
(int[]) iv[0],
(Object[]) iv[1]
)
);
} else throw new IllegalArgumentException(
"Members of type '" + t.getName() + "' are not yet supported by " + Structures.class.getName()
);
} else if (RefStruct.class.isAssignableFrom(t) && v instanceof Map,?>) {
return Structures.create(
this.persistenceManager,
t,
(Map,?>)v
);
} else {
return v;
}
}
Object[] toNestedArrays(
Object[] source
) {
if (source.length != this.metaData.names.length) throw new IllegalArgumentException(
"Structure " + this.metaData.type + " has " + this.metaData.names.length +
" members, but " + source.length + " have been provided"
);
Object[] target = source;
for (
int slot = 0;
slot < source.length;
slot++
) {
Object value = source[slot];
if(value == null) {
target[slot] = null;
} else {
Class> memberType = this.metaData.memberTypes[slot];
if(RefStruct_1_0.class.isAssignableFrom(memberType)) {
target[slot] = ((RefStruct_1_0)value).refDelegate();
} else if(List.class == memberType || Set.class == memberType) {
if (source == target) {
target = new Object[source.length];
System.arraycopy(source, 0, target, 0, slot);
}
Collection> c;
if(value instanceof Collection>) {
c = (Collection>) value;
} else if (value.getClass().isArray()) {
c = ArraysExtension.asList(value);
} else throw new IllegalArgumentException(
"Member '" + this.metaData.names[slot] +
"' requires either a collection or array value"
);
int s = c.size();
if(s == 0) {
target[slot] = null;
} else {
Object[] vs = new Object[s];
int j = 0;
for(Object e : c) {
vs[j++] = toElement(slot, e);
}
target[slot] = vs;
}
} else if (SparseArray.class == memberType) {
if (source == target) {
target = new Object[source.length];
System.arraycopy(source, 0, target, 0, slot);
}
SparseArray> a;
if (value instanceof SparseArray>) {
a = (SparseArray>) value;
} else if (value instanceof Map,?>) {
a = toSparseArray((Map,?>)value);
} else throw new IllegalArgumentException(
"Member '" + this.metaData.names[slot] +
"' requires a map value with numeric keys"
);
int s = a.size();
if (s == 0) {
target[slot] = null;
} else {
int[] is = new int[s];
Object[] vs = new Object[s];
int j = 0;
for (
ListIterator> p = a.populationIterator();
p.hasNext();
j++
) {
is[j] = p.nextIndex();
vs[j] = toElement(slot, p.next());
}
target[slot] = new Object[] {
is, vs
};
}
} else if(Classes.toObjectClass(memberType).isInstance(value)) {
target[slot] = value;
} else throw new IllegalArgumentException(
"Value of class '" + value.getClass().getName() +
"' can't be assigned to member '" + this.metaData.names[slot] + "'" +
" expecting a value of type '" + memberType.getName() + "'"
);
}
}
return target;
}
/**
*
*/
private final static SparseArray> toSparseArray(
Map,?> source
){
SparseArray
© 2015 - 2024 Weber Informatics LLC | Privacy Policy