/*******************************************************************************
* Copyright © 2011, 2013 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*
*******************************************************************************/
package org.eclipse.edt.runtime.java.eglx.lang;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import org.eclipse.edt.javart.AnyBoxedObject;
import org.eclipse.edt.javart.Constants;
import org.eclipse.edt.javart.TypeConstraints;
import org.eclipse.edt.javart.messages.Message;
import org.eclipse.edt.javart.util.JavartUtil;
import eglx.lang.AnyException;
import eglx.lang.DynamicAccessException;
import eglx.lang.NullValueException;
import eglx.lang.TypeCastException;
public abstract class EAny implements eglx.lang.EAny {
/**
* The version ID used in serialization.
*/
private static final long serialVersionUID = Constants.SERIAL_VERSION_UID;
public static eglx.lang.EAny box(eglx.lang.EAny value) throws AnyException {
return ezeBox(value);
}
@SuppressWarnings("unchecked")
public static <R extends Object> AnyBoxedObject<R> ezeBox(R object) throws AnyException {
if (object instanceof AnyValue) {
AnyValue newValue = ((AnyValue) object).ezeNewValue();
newValue.ezeCopy((AnyValue) object);
return new AnyBoxedObject<R>((R) newValue);
}
return new AnyBoxedObject<R>(object);
}
public static <R extends Object> AnyBoxedObject<R> ezeWrap(R object) {
return new AnyBoxedObject<R>(object);
}
public static <T extends Object> T ezeCast(Object value, Class<T> clazz) throws AnyException {
try {
Object unboxed = value instanceof eglx.lang.EAny ? ((eglx.lang.EAny) value).ezeUnbox() : value;
return clazz.cast(unboxed);
}
catch (ClassCastException ex) {
TypeCastException tcx = new TypeCastException();
tcx.castToName = clazz.getName();
Object unboxed = value instanceof eglx.lang.EAny ? ((eglx.lang.EAny) value).ezeUnbox() : value;
tcx.actualTypeName = unboxed.getClass().getName();
tcx.initCause(ex);
throw tcx.fillInMessage(Message.CONVERSION_ERROR, value, tcx.actualTypeName, tcx.castToName);
}
}
/**
* Dynamic implementation of data type conversion. Used when casting values of unknown type to the receiver type. This
* method looks up the appropriate conversion operation first in the receiver class and if not found then lookup will
* continue into the class of the <code>value</code> parameter.
* @param value
* @param conversionMethod
* @param clazz
* @param parameterTypes
* @param args
* @return
* @throws AnyException
*/
@SuppressWarnings("unchecked")
public static Object ezeCast(Object value, String conversionMethod, Class clazz, Class[] parameterTypes, Object[] args) throws AnyException {
try {
// first try finding the method if it is boxed
if (value instanceof eglx.lang.EAny) {
Method method = null;
Class[] parmTypes = new Class[parameterTypes == null ? 1 : parameterTypes.length + 1];
parmTypes[0] = value.getClass();
if (parameterTypes != null)
java.lang.System.arraycopy(parameterTypes, 0, parmTypes, 1, parameterTypes.length);
try {
method = clazz.getMethod(conversionMethod, parmTypes);
}
catch (NoSuchMethodException ex) {
try {
method = value.getClass().getMethod(conversionMethod, parmTypes);
}
catch (Exception ex1) {
}
}
if (method != null) {
if (args == null)
return method.invoke(null, value);
else
return method.invoke(null, value, args);
}
}
// Conversion operation needs to be invoked
Object unboxed = value instanceof eglx.lang.EAny ? ((eglx.lang.EAny) value).ezeUnbox() : value;
if (unboxed == null) {
return null;
}
Method method;
Class[] parmTypes = new Class[parameterTypes == null ? 1 : parameterTypes.length + 1];
parmTypes[0] = unboxed.getClass();
if (parameterTypes != null)
java.lang.System.arraycopy(parameterTypes, 0, parmTypes, 1, parameterTypes.length);
try {
method = clazz.getMethod(conversionMethod, parmTypes);
}
catch (NoSuchMethodException ex) {
try {
method = value.getClass().getMethod(conversionMethod, parmTypes);
}
catch (NoSuchMethodException ex1) {
TypeCastException tcx = new TypeCastException();
tcx.castToName = clazz.getName();
tcx.actualTypeName = unboxed.getClass().getName();
throw tcx.fillInMessage(Message.CONVERSION_ERROR, value, tcx.actualTypeName, tcx.castToName);
}
}
if (args == null)
return method.invoke(null, unboxed);
else
return method.invoke(null, unboxed, args);
}
catch (InvocationTargetException ex) {
throw JavartUtil.makeEglException(ex.getTargetException());
}
catch (Exception ex1) {
// Should not ever get here
throw JavartUtil.makeEglException(ex1);
}
}
public static <T extends Object> boolean ezeIsa(Object object, Class<T> clazz) {
Object unboxed = object instanceof eglx.lang.EAny ? ((eglx.lang.EAny) object).ezeUnbox() : object;
return clazz.isInstance(unboxed);
}
public static Object ezeDeepCopy(Object object) {
if (object instanceof EAny) {
EAny any = (EAny) object;
// TODO Using clone is not the correct way to make a deep copy of some kinds of objects.
try {
return any.clone();
}
catch (CloneNotSupportedException ex) {
AnyException anyEx = new AnyException();
anyEx.setMessage(ex.toString());
throw anyEx;
}
}
return object;
}
public EAny() {
super();
}
public java.lang.String ezeTypeSignature() {
return getClass().getName();
}
public String ezeName() {
return getClass().getSimpleName();
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
public static Object ezeGet(Object obj, String name) throws AnyException {
if (obj == null) {
NullValueException nvx = new NullValueException();
throw nvx.fillInMessage( Message.NULL_NOT_ALLOWED );
} else if (obj instanceof eglx.lang.EAny) {
return ((eglx.lang.EAny)obj).ezeGet(name);
} else {
try {
Field field = obj.getClass().getField(name);
return field.get(obj);
}
catch (Exception e) {
// Ignore this Exception and throw a DynamicAccessException, below.
}
}
DynamicAccessException dax = new DynamicAccessException();
dax.key = name;
throw dax.fillInMessage(Message.DYNAMIC_ACCESS_FAILED, name, obj);
}
public static Object ezeGet(Object obj, int index) throws AnyException {
if (obj == null) {
NullValueException nvx = new NullValueException();
throw nvx.fillInMessage( Message.NULL_NOT_ALLOWED );
}
if (obj instanceof eglx.lang.EAny)
return ((eglx.lang.EAny)obj).ezeGet(index);
DynamicAccessException dax = new DynamicAccessException();
dax.key = "" + index;
throw dax.fillInMessage(Message.DYNAMIC_ACCESS_FAILED, "" + index, obj);
}
public static void ezeSet(Object obj, String name, Object value) throws AnyException {
if (obj == null) {
NullValueException nvx = new NullValueException();
throw nvx.fillInMessage( Message.NULL_NOT_ALLOWED );
}
if (obj instanceof eglx.lang.EAny) {
((eglx.lang.EAny)obj).ezeSet(name, value);
return;
}
DynamicAccessException dax = new DynamicAccessException();
dax.key = name;
throw dax.fillInMessage(Message.DYNAMIC_ACCESS_FAILED, name, obj);
}
public Object ezeGet(String name) throws AnyException {
try {
Field field = this.getClass().getField(name);
if (ezeTypeConstraints(name) != null) {
TypeConstraints constraints = ezeTypeConstraints(name);
Object value = field.get(this);
return constraints.box(value);
}
Object value = field.get(this);
return value;
}
catch (Exception e) {
DynamicAccessException dax = new DynamicAccessException();
dax.key = name;
dax.initCause(e);
throw dax.fillInMessage(Message.DYNAMIC_ACCESS_FAILED, name, this);
}
}
public Object ezeGet(int index) throws AnyException {
Object unboxed = ezeUnbox();
if (unboxed instanceof List)
return EAny.asAny(((List<?>) unboxed).get(index));
else {
TypeCastException tcx = new TypeCastException();
tcx.castToName = "list";
tcx.actualTypeName = unboxed.getClass().getName();
throw tcx.fillInMessage( Message.CONVERSION_ERROR, unboxed, tcx.actualTypeName,
tcx.castToName );
}
}
@SuppressWarnings("unchecked")
public void ezeSet(String name, Object value) throws AnyException {
try {
Field field = this.getClass().getField(name);
if (ezeTypeConstraints(name) != null) {
TypeConstraints constraints = ezeTypeConstraints(name);
value = constraints.constrainValue(value);
} else if (value instanceof AnyBoxedObject) {
value = field.getType().getName().equalsIgnoreCase("eglx.lang.EAny") ? value : ((AnyBoxedObject<Object>) value).ezeUnbox();
}
field.set(this, value);
}
catch (Exception e) {
DynamicAccessException dax = new DynamicAccessException();
dax.key = name;
dax.initCause(e);
throw dax.fillInMessage(Message.DYNAMIC_ACCESS_FAILED, name, this);
}
}
@SuppressWarnings("unchecked")
@Override
public eglx.lang.EAny ezeUnbox() {
return this;
}
public static Object asAny(Object value) {
return value;
}
public static boolean equals(Object object1, Object object2) {
Object unboxedOp1 = object1 instanceof eglx.lang.EAny ? ((eglx.lang.EAny) object1).ezeUnbox() : object1;
Object unboxedOp2 = object2 instanceof eglx.lang.EAny ? ((eglx.lang.EAny) object2).ezeUnbox() : object2;
if (unboxedOp1 == unboxedOp2)
return true;
if (unboxedOp1 == null || unboxedOp2 == null)
return false;
if (object1 instanceof eglx.lang.ENumber && object2 instanceof eglx.lang.ENumber)
return ENumber.equals(object1, object2);
if (unboxedOp1 instanceof Number && unboxedOp2 instanceof Number)
return ENumber.equals((Number) unboxedOp1, (Number) unboxedOp2);
return unboxedOp1.equals(unboxedOp2);
}
public static boolean notEquals(Object object1, Object object2) {
return !equals(object1, object2);
}
/**
* Subclasses of this method that contain fields with types that can be parameterized with constraints such as length,
* precision decimals, pattern, etc. should override this method to return the particular constraints set each field.
* These constraints are used to convert values to conform to the given constraints. An example would be a string of
* length 10 being dynamically assigned to a String field that was defined as char(6). The actual java field type will be
* java.lang.String. However the TypeConstraints for that field will have a reference to Class egl.lang.Char.class as
* well as the length of 6. This information will be used to then truncated the string of length 10 to one of length 6.
* @param fieldName
* @return
*/
public TypeConstraints ezeTypeConstraints(String fieldName) {
return null;
}
/**
* Default implementation does nothing
*/
public void ezeInitialize() throws AnyException {
}
}