Java tutorial
/* * Copyright 2010 Red Hat, Inc. and/or its affiliates. * * 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.drools.core.xml.jaxb.util; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import javax.xml.bind.annotation.adapters.XmlAdapter; import org.drools.core.QueryResultsImpl; import org.drools.core.common.DisconnectedFactHandle; import org.drools.core.runtime.rule.impl.FlatQueryResults; import org.drools.core.xml.jaxb.util.JaxbListWrapper.JaxbWrapperType; import org.kie.api.runtime.rule.FactHandle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Do not return an instance of Arrays.asList() -- that implementation is *not* * modifiable! * * 7.0 plans: * - move this at least to kie-internal * - use JaxbObjectObjectPair instead of JaxbStringObjectPair for maps */ @SuppressWarnings("unchecked") public class JaxbUnknownAdapter extends XmlAdapter<Object, Object> { private static final Logger logger = LoggerFactory.getLogger(JaxbUnknownAdapter.class); private static final Object PRESENT = new Object(); @Override public Object marshal(Object o) throws Exception { try { return recursiveMarshal(o, new IdentityHashMap<Object, Object>()); } catch (Exception e) { // because exceptions are always swallowed by JAXB logger.error("Unable to marshal " + o.getClass().getName() + " instance: " + e.getMessage(), e); throw e; } } private Object recursiveMarshal(Object o, Map<Object, Object> seenObjectsMap) { if (o == null) { return o; } if (seenObjectsMap.put(o, PRESENT) != null) { throw new UnsupportedOperationException("Serialization of recursive data structures is not supported!"); } try { if (o instanceof List) { List list = (List) o; Object[] serializedArr = convertCollectionToSerializedArray(list, seenObjectsMap); return new JaxbListWrapper(serializedArr, JaxbWrapperType.LIST); } else if (o instanceof Set) { Set set = (Set) o; Object[] serializedArr = convertCollectionToSerializedArray(set, seenObjectsMap); return new JaxbListWrapper(serializedArr, JaxbWrapperType.SET); } else if (o instanceof Map) { Map<Object, Object> map = (Map<Object, Object>) o; List<JaxbStringObjectPair> pairList = new ArrayList<JaxbStringObjectPair>(map.size()); if (map == null || map.isEmpty()) { pairList = Collections.EMPTY_LIST; } for (Entry<Object, Object> entry : map.entrySet()) { Object key = entry.getKey(); if (key != null && !(key instanceof String)) { throw new UnsupportedOperationException( "Only String keys for Map structures are supported [key was a " + key.getClass().getName() + "]"); } // There's already a @XmlJavaTypeAdapter(JaxbUnknownAdapter.class) anno on the JaxbStringObjectPair.value field pairList.add(new JaxbStringObjectPair((String) key, entry.getValue())); } return new JaxbListWrapper(pairList.toArray(new JaxbStringObjectPair[pairList.size()]), JaxbWrapperType.MAP); } else if (o.getClass().isArray()) { // convert to serializable types int length = Array.getLength(o); Object[] serializedArr = new Object[length]; for (int i = 0; i < length; ++i) { Object elem = convertObjectToSerializableVariant(Array.get(o, i), seenObjectsMap); serializedArr[i] = elem; } // convert to JaxbListWrapper JaxbListWrapper wrapper = new JaxbListWrapper(serializedArr, JaxbWrapperType.ARRAY); Class componentType = o.getClass().getComponentType(); String componentTypeName = o.getClass().getComponentType().getCanonicalName(); if (componentTypeName == null) { throw new UnsupportedOperationException( "Local or anonymous classes are not supported for serialization: " + componentType.getName()); } wrapper.setComponentType(componentTypeName); return wrapper; } else { return o; } } finally { seenObjectsMap.remove(o); } } private Object[] convertCollectionToSerializedArray(Collection collection, Map<Object, Object> seenObjectsMap) { List<Object> serializedList = new ArrayList<Object>(collection.size()); for (Object elem : collection) { elem = convertObjectToSerializableVariant(elem, seenObjectsMap); serializedList.add(elem); } return serializedList.toArray(new Object[serializedList.size()]); } private Object convertObjectToSerializableVariant(Object obj, Map<Object, Object> seenObjectsMap) { if (obj == null) { return null; } if (obj instanceof QueryResultsImpl) { obj = new FlatQueryResults((QueryResultsImpl) obj); } else if (obj instanceof FactHandle) { obj = DisconnectedFactHandle.newFrom((FactHandle) obj); } else if (!(obj instanceof JaxbListWrapper) && (obj instanceof Collection || obj instanceof Map)) { obj = recursiveMarshal(obj, seenObjectsMap); } return obj; } @Override public Object unmarshal(Object o) throws Exception { try { return recursiveUnmarhsal(o); } catch (Exception e) { // because exceptions are always swallowed by JAXB logger.error("Unable to *un*marshal " + o.getClass().getName() + " instance: " + e.getMessage(), e); throw e; } } public Object recursiveUnmarhsal(Object o) throws Exception { if (o instanceof JaxbListWrapper) { JaxbListWrapper wrapper = (JaxbListWrapper) o; Object[] elements = wrapper.getElements(); int size = 0; if (elements != null) { size = elements.length; } if (wrapper.getType() == null) { List<Object> list = new ArrayList<Object>(size); return convertSerializedElementsToCollection(elements, list); } else { switch (wrapper.getType()) { case LIST: List<Object> list = new ArrayList<Object>(size); return convertSerializedElementsToCollection(elements, list); case SET: Set<Object> set = new HashSet<Object>(size); return convertSerializedElementsToCollection(elements, set); case MAP: Map<String, Object> map = new HashMap<String, Object>(size); if (size > 0) { for (Object keyValueObj : elements) { JaxbStringObjectPair keyValue = (JaxbStringObjectPair) keyValueObj; Object key = keyValue.getKey(); Object value = convertSerializedObjectToObject(keyValue.getValue()); map.put(key.toString(), value); } } return map; case ARRAY: Object[] objArr = wrapper.getElements(); int length = objArr.length; String componentTypeName = wrapper.getComponentType(); Class realArrComponentType = null; realArrComponentType = getClass(componentTypeName); // create and fill array Object realArr = Array.newInstance(realArrComponentType, objArr.length); for (int i = 0; i < length; ++i) { Array.set(realArr, i, convertSerializedObjectToObject(objArr[i])); } return realArr; default: throw new IllegalArgumentException( "Unknown JAXB collection wrapper type: " + wrapper.getType().toString()); } } } else if (o instanceof JaxbStringObjectPair[]) { // backwards compatibile: remove in 7.0.x JaxbStringObjectPair[] value = (JaxbStringObjectPair[]) o; Map<Object, Object> r = new HashMap<Object, Object>(); for (JaxbStringObjectPair p : value) { if (p.getValue() instanceof JaxbListWrapper) { r.put(p.getKey(), new ArrayList(Arrays.asList(((JaxbListWrapper) p.getValue()).getElements()))); } else { r.put(p.getKey(), p.getValue()); } } return r; } else { return o; } } // idea stolen from org.apache.commons.lang3.ClassUtils private static final Map<String, String> classToArrayTypeMap = new HashMap<String, String>(); static { classToArrayTypeMap.put("int", "I"); classToArrayTypeMap.put("boolean", "Z"); classToArrayTypeMap.put("float", "F"); classToArrayTypeMap.put("long", "J"); classToArrayTypeMap.put("short", "S"); classToArrayTypeMap.put("byte", "B"); classToArrayTypeMap.put("double", "D"); classToArrayTypeMap.put("char", "C"); } private static Class getClass(String className) throws Exception { ClassLoader tccl = Thread.currentThread().getContextClassLoader(); ClassLoader loader = tccl == null ? JaxbUnknownAdapter.class.getClassLoader() : tccl; try { // String.contains() will be cheaper/faster than Map.contains() if (className.contains(".")) { return Class.forName(className, true, loader); } else { // Thanks, org.apache.commons.lang3.ClassUtils! String arrClassName = classToArrayTypeMap.get(className); if (arrClassName == null) { throw new IllegalStateException( "Unexpected class type encountered during deserialization: " + arrClassName); } arrClassName = "[" + arrClassName; return Class.forName(arrClassName, true, loader).getComponentType(); } } catch (ClassNotFoundException cnfe) { throw new IllegalStateException( "Class '" + className + "' could not be found during deserialization: " + cnfe.getMessage(), cnfe); } } private Collection convertSerializedElementsToCollection(Object[] elements, Collection collection) throws Exception { List<Object> list; if (elements == null) { list = Collections.EMPTY_LIST; } else { list = new ArrayList<Object>(elements.length); for (Object elem : elements) { elem = convertSerializedObjectToObject(elem); list.add(elem); } } collection.addAll(list); return collection; } private Object convertSerializedObjectToObject(Object element) throws Exception { if (element == null) { return element; } if (element instanceof JaxbListWrapper) { element = unmarshal(element); } return element; } }