blob: bce6a9cf8acaded6adbe7af3ce0c379ce0eb2f30 [file] [log] [blame]
/*
* Copyright 2000-2013 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.JDOMUtil;
import com.intellij.util.xmlb.annotations.AbstractCollection;
import org.jdom.Content;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
abstract class AbstractCollectionBinding implements Binding {
private Map<Class, Binding> myElementBindings;
private final Class myElementType;
private final String myTagName;
@Nullable protected final Accessor myAccessor;
private final AbstractCollection myAnnotation;
public AbstractCollectionBinding(Class elementType, String tagName, @Nullable Accessor accessor) {
myElementType = elementType;
myTagName = tagName;
myAccessor = accessor;
myAnnotation = accessor == null ? null : XmlSerializerImpl.findAnnotation(accessor.getAnnotations(), AbstractCollection.class);
}
@Override
public void init() {
if (myAnnotation != null) {
if (!myAnnotation.surroundWithTag()) {
if (myAnnotation.elementTag() == null) {
throw new XmlSerializationException("If surround with tag is turned off, element tag must be specified for: " + myAccessor);
}
if (myAnnotation.elementTag().equals(Constants.OPTION)) {
for (Binding binding : getElementBindings().values()) {
if (binding instanceof TagBindingWrapper) {
throw new XmlSerializationException("If surround with tag is turned off, element tag must be specified for: " + myAccessor);
}
}
}
}
}
}
protected Binding getElementBinding(Class<?> elementClass) {
final Binding binding = getElementBindings().get(elementClass);
return binding == null ? XmlSerializerImpl.getBinding(elementClass) : binding;
}
private synchronized Map<Class, Binding> getElementBindings() {
if (myElementBindings == null) {
myElementBindings = new HashMap<Class, Binding>();
myElementBindings.put(myElementType, getBinding(myElementType));
if (myAnnotation != null) {
for (Class aClass : myAnnotation.elementTypes()) {
myElementBindings.put(aClass, getBinding(aClass));
}
}
}
return myElementBindings;
}
protected Binding getElementBinding(Object node) {
for (Binding binding : getElementBindings().values()) {
if (binding.isBoundTo(node)) return binding;
}
throw new XmlSerializationException("Node " + node + " is not bound");
}
private Binding getBinding(final Class type) {
Binding binding = XmlSerializerImpl.getBinding(type);
return binding.getBoundNodeType().isAssignableFrom(Element.class) ? binding : createElementTagWrapper(binding);
}
private Binding createElementTagWrapper(final Binding elementBinding) {
if (myAnnotation == null) return new TagBindingWrapper(elementBinding, Constants.OPTION, Constants.VALUE);
return new TagBindingWrapper(elementBinding,
myAnnotation.elementTag() != null ? myAnnotation.elementTag() : Constants.OPTION,
myAnnotation.elementValueAttribute() != null ? myAnnotation.elementValueAttribute() : Constants.VALUE);
}
abstract Object processResult(Collection result, Object target);
abstract Iterable getIterable(Object o);
@Override
public Object serialize(Object o, Object context, SerializationFilter filter) {
Iterable iterable = getIterable(o);
if (iterable == null) return context;
final String tagName = getTagName(o);
if (tagName != null) {
Element result = new Element(tagName);
for (Object e : iterable) {
if (e == null) {
throw new XmlSerializationException("Collection " + myAccessor + " contains 'null' object");
}
final Binding binding = getElementBinding(e.getClass());
result.addContent((Content)binding.serialize(e, result, filter));
}
return result;
}
else {
List<Object> result = new ArrayList<Object>();
for (Object e : iterable) {
final Binding binding = getElementBinding(e.getClass());
result.add(binding.serialize(e, result, filter));
}
return result;
}
}
@Override
public Object deserialize(Object o, @NotNull Object... nodes) {
Collection result;
if (getTagName(o) != null) {
assert nodes.length == 1;
Element e = (Element)nodes[0];
result = createCollection(e.getName());
final Content[] childElements = JDOMUtil.getContent(e);
for (final Content n : childElements) {
if (XmlSerializerImpl.isIgnoredNode(n)) continue;
final Binding elementBinding = getElementBinding(n);
Object v = elementBinding.deserialize(o, n);
//noinspection unchecked
result.add(v);
}
}
else {
result = new ArrayList();
for (Object node : nodes) {
if (XmlSerializerImpl.isIgnoredNode(node)) continue;
final Binding elementBinding = getElementBinding(node);
Object v = elementBinding.deserialize(o, node);
//noinspection unchecked
result.add(v);
}
}
return processResult(result, o);
}
protected Collection createCollection(final String tagName) {
return new ArrayList();
}
@Override
public boolean isBoundTo(Object node) {
if (!(node instanceof Element)) return false;
final String tagName = getTagName(node);
if (tagName == null) {
for (Binding binding : getElementBindings().values()) {
if (binding.isBoundTo(node)) return true;
}
}
return ((Element)node).getName().equals(tagName);
}
@Override
public Class getBoundNodeType() {
return Element.class;
}
public Class getElementType() {
return myElementType;
}
@Nullable
private String getTagName(final Object target) {
if (myAnnotation == null || myAnnotation.surroundWithTag()) return getCollectionTagName(target);
return null;
}
protected String getCollectionTagName(final Object target) {
return myTagName;
}
}