| /* |
| * 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); |
| } |
| } |
| } |