blob: 6cc2124c695a3e722bb2eb9feb15e011f2cc05b5 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* 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 com.intellij.util.xmlb;
import com.intellij.openapi.util.Pair;
import com.intellij.util.ReflectionUtil;
import org.jdom.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.annotation.Annotation;
import java.lang.ref.SoftReference;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author mike
*/
class XmlSerializerImpl {
private final SerializationFilter filter;
private static SoftReference<Map<Pair<Type, Accessor>, Binding>> ourBindings;
public XmlSerializerImpl(SerializationFilter filter) {
this.filter = filter;
}
Element serialize(@NotNull Object object) throws XmlSerializationException {
try {
return (Element)getBinding(object.getClass()).serialize(object, null, filter);
}
catch (XmlSerializationException e) {
throw e;
}
catch (Exception e) {
throw new XmlSerializationException("Can't serialize instance of " + object.getClass(), e);
}
}
static Binding getBinding(Type type) {
return getTypeBinding(type, null);
}
static Binding getBinding(Accessor accessor) {
return getTypeBinding(accessor.getGenericType(), accessor);
}
static Binding getTypeBinding(Type type, @Nullable Accessor accessor) {
if (type instanceof Class) {
return _getClassBinding((Class<?>)type, type, accessor);
}
if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType)type;
Type rawType = parameterizedType.getRawType();
assert rawType instanceof Class;
return _getClassBinding((Class<?>)rawType, type, accessor);
}
throw new UnsupportedOperationException("Can't get binding for: " + type);
}
private static synchronized Binding _getClassBinding(Class<?> aClass, Type originalType, @Nullable Accessor accessor) {
final Pair<Type, Accessor> p = Pair.create(originalType, accessor);
Map<Pair<Type, Accessor>, Binding> map = getBindingCacheMap();
Binding binding = map.get(p);
if (binding == null) {
binding = _getNonCachedClassBinding(aClass, accessor, originalType);
map.put(p, binding);
binding.init();
}
return binding;
}
private static Map<Pair<Type, Accessor>, Binding> getBindingCacheMap() {
Map<Pair<Type, Accessor>, Binding> map = com.intellij.reference.SoftReference.dereference(ourBindings);
if (map == null) {
map = new ConcurrentHashMap<Pair<Type, Accessor>, Binding>();
ourBindings = new SoftReference<Map<Pair<Type, Accessor>, Binding>>(map);
}
return map;
}
private static Binding _getNonCachedClassBinding(final Class<?> aClass, @Nullable Accessor accessor, final Type originalType) {
if (aClass.isPrimitive()) return new PrimitiveValueBinding(aClass);
if (aClass.isArray()) {
return Element.class.isAssignableFrom(aClass.getComponentType())
? new JDOMElementBinding(accessor) : new ArrayBinding(aClass, accessor);
}
if (Number.class.isAssignableFrom(aClass)) return new PrimitiveValueBinding(aClass);
if (Boolean.class.isAssignableFrom(aClass)) return new PrimitiveValueBinding(aClass);
if (String.class.isAssignableFrom(aClass)) return new PrimitiveValueBinding(aClass);
if (Collection.class.isAssignableFrom(aClass) && originalType instanceof ParameterizedType) {
return new CollectionBinding((ParameterizedType)originalType, accessor);
}
if (Map.class.isAssignableFrom(aClass) && originalType instanceof ParameterizedType) {
return new MapBinding((ParameterizedType)originalType, accessor);
}
if (Element.class.isAssignableFrom(aClass)) return new JDOMElementBinding(accessor);
if (Date.class.isAssignableFrom(aClass)) return new DateBinding();
if (aClass.isEnum()) return new PrimitiveValueBinding(aClass);
return new BeanBinding(aClass, accessor);
}
@Nullable
@SuppressWarnings({"unchecked"})
static <T> T findAnnotation(Annotation[] annotations, Class<T> aClass) {
if (annotations == null) return null;
for (Annotation annotation : annotations) {
if (aClass.isAssignableFrom(annotation.getClass())) return (T)annotation;
}
return null;
}
@Nullable
@SuppressWarnings({"unchecked"})
static <T> T convert(Object value, Class<T> type) {
if (value == null) return null;
if (type.isInstance(value)) return (T)value;
if (String.class.isAssignableFrom(type)) return (T)String.valueOf(value);
if (int.class.isAssignableFrom(type) || Integer.class.isAssignableFrom(type)) return (T)Integer.valueOf(String.valueOf(value));
if (double.class.isAssignableFrom(type) || Double.class.isAssignableFrom(type)) return (T)Double.valueOf(String.valueOf(value));
if (float.class.isAssignableFrom(type) || Float.class.isAssignableFrom(type)) return (T)Float.valueOf(String.valueOf(value));
if (long.class.isAssignableFrom(type) || Long.class.isAssignableFrom(type)) return (T)Long.valueOf(String.valueOf(value));
if (boolean.class.isAssignableFrom(type) || Boolean.class.isAssignableFrom(type)) return (T)Boolean.valueOf(String.valueOf(value));
if (type.isEnum()) {
final T[] enumConstants = type.getEnumConstants();
for (T enumConstant : enumConstants) {
if (enumConstant.toString().equals(value.toString())) return enumConstant;
}
return null;
}
throw new XmlSerializationException("Can't covert " + value.getClass() + " into " + type);
}
public static boolean isIgnoredNode(final Object child) {
if (child instanceof Text && ((Text)child).getValue().trim().isEmpty()) {
return true;
}
if (child instanceof Comment) {
return true;
}
if (child instanceof Attribute) {
Attribute attr = (Attribute)child;
final String namespaceURI = attr.getNamespaceURI();
if (namespaceURI != null && !namespaceURI.isEmpty()) return true;
}
return false;
}
public static Content[] getNotIgnoredContent(final Element m) {
List<Content> result = new ArrayList<Content>();
final List content = m.getContent();
for (Object o : content) {
if (!isIgnoredNode(o)) result.add((Content)o);
}
return result.toArray(new Content[result.size()]);
}
/**
* {@link Class#newInstance()} cannot instantiate private classes
*/
static <T> T newInstance(@NotNull Class<T> aClass) {
try {
return ReflectionUtil.newInstance(aClass);
}
catch (Exception e) {
throw new XmlSerializationException(e);
}
}
}