unify multi nodes handling

This commit is contained in:
Vladimir Krivosheev
2014-11-08 07:45:28 +01:00
parent 2ac5955e49
commit 63f4ceb77e
16 changed files with 151 additions and 125 deletions
@@ -28,7 +28,7 @@ import java.util.Collection;
import java.util.List;
import java.util.Map;
abstract class AbstractCollectionBinding extends Binding {
abstract class AbstractCollectionBinding extends Binding implements MultiNodeBinding {
private Map<Class, Binding> myElementBindings;
private final Class myElementType;
@@ -43,6 +43,11 @@ abstract class AbstractCollectionBinding extends Binding {
myAnnotation = accessor == null ? null : accessor.getAnnotation(AbstractCollection.class);
}
@Override
public boolean isMulti() {
return true;
}
@Override
public void init() {
if (myAnnotation != null) {
@@ -21,7 +21,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.List;
class AccessorBindingWrapper extends Binding {
class AccessorBindingWrapper extends Binding implements MultiNodeBinding {
private final Binding myBinding;
public AccessorBindingWrapper(@NotNull Accessor accessor, @NotNull Binding binding) {
@@ -64,7 +64,7 @@ class AccessorBindingWrapper extends Binding {
((BeanBinding)myBinding).deserializeInto(currentValue, (Element)nodes.get(0), null);
}
else {
Object deserializedValue = myBinding.deserializeList(currentValue, nodes);
Object deserializedValue = Binding.deserializeList(myBinding, currentValue, nodes);
if (currentValue != deserializedValue) {
myAccessor.write(context, deserializedValue);
}
@@ -72,6 +72,11 @@ class AccessorBindingWrapper extends Binding {
return context;
}
@Override
public boolean isMulti() {
return myBinding instanceof MultiNodeBinding && ((MultiNodeBinding)myBinding).isMulti();
}
@Override
public boolean isBoundTo(Object node) {
return myBinding.isBoundTo(node);
@@ -21,8 +21,6 @@ import org.jdom.Text;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class AttributeBinding extends BasePrimitiveBinding {
public AttributeBinding(@NotNull Accessor accessor, @NotNull Attribute attribute) {
super(accessor, attribute.value(), attribute.converter());
@@ -52,13 +50,6 @@ public class AttributeBinding extends BasePrimitiveBinding {
return new org.jdom.Attribute(myName, stringValue);
}
@Nullable
@Override
public Object deserializeList(Object context, @NotNull List<?> nodes) {
assert nodes.size() == 1;
return deserialize(context, nodes.get(0));
}
@Override
@Nullable
public Object deserialize(Object context, @NotNull Object node) {
@@ -127,26 +127,6 @@ class BeanBinding extends Binding {
return instance;
}
@Nullable
@Override
public Object deserializeList(Object context, @NotNull List<?> nodes) {
Element element = null;
for (Object aNode : nodes) {
if (!XmlSerializerImpl.isIgnoredNode(aNode)) {
element = (Element)aNode;
break;
}
}
if (element == null) {
return context;
}
Object instance = ReflectionUtil.newInstance(myBeanClass);
deserializeInto(instance, element, null);
return instance;
}
@NotNull
public Binding[] computeOrderedBindings(@NotNull LinkedHashSet<String> accessorNameTracker) {
final TObjectDoubleHashMap<String> weights = new TObjectDoubleHashMap<String>(accessorNameTracker.size());
@@ -182,7 +162,7 @@ class BeanBinding extends Binding {
}
public void deserializeInto(@NotNull Object result, @NotNull Element element, @Nullable Set<String> accessorNameTracker) {
MultiMap<Binding, Object> data = MultiMap.createLinked();
MultiMap<Binding, Object> data = null;
nextNode:
for (Object child : ContainerUtil.concat(element.getContent(), element.getAttributes())) {
if (XmlSerializerImpl.isIgnoredNode(child)) {
@@ -191,7 +171,18 @@ class BeanBinding extends Binding {
for (Binding binding : myBindings) {
if (binding.isBoundTo(child)) {
data.putValue(binding, child);
if (binding instanceof MultiNodeBinding && ((MultiNodeBinding)binding).isMulti()) {
if (data == null) {
data = MultiMap.createLinked();
}
data.putValue(binding, child);
}
else {
if (accessorNameTracker != null) {
accessorNameTracker.add(binding.getAccessor().getName());
}
binding.deserialize(result, child);
}
continue nextNode;
}
}
@@ -202,11 +193,13 @@ class BeanBinding extends Binding {
Logger.getInstance("#" + myBeanClass.getName()).debug(message);
}
for (Binding binding : data.keySet()) {
if (accessorNameTracker != null) {
accessorNameTracker.add(binding.getAccessor().getName());
if (data != null) {
for (Binding binding : data.keySet()) {
if (accessorNameTracker != null) {
accessorNameTracker.add(binding.getAccessor().getName());
}
((MultiNodeBinding)binding).deserializeList(result, (List<?>)data.get(binding));
}
binding.deserializeList(result, (List<?>)data.get(binding));
}
}
@@ -38,13 +38,22 @@ abstract class Binding {
@Nullable
public abstract Object deserialize(Object context, @NotNull Object node);
@Nullable
public abstract Object deserializeList(Object context, @NotNull List<?> nodes);
public abstract boolean isBoundTo(Object node);
public abstract Class getBoundNodeType();
public void init() {
}
@SuppressWarnings("CastToIncompatibleInterface")
@Nullable
public static Object deserializeList(@NotNull Binding binding, Object context, @NotNull List<?> nodes) {
if (binding instanceof MultiNodeBinding) {
return ((MultiNodeBinding)binding).deserializeList(context, nodes);
}
else {
assert nodes.size() == 1;
return binding.deserialize(context, nodes.get(0));
}
}
}
@@ -44,4 +44,9 @@ public class DateBinding extends PrimitiveValueBinding {
return new Date(0);
}
}
@Override
public boolean isMulti() {
return false;
}
}
@@ -16,7 +16,6 @@
package com.intellij.util.xmlb;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.SmartList;
import com.intellij.util.xmlb.annotations.Tag;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
@@ -25,7 +24,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
class JDOMElementBinding extends Binding {
class JDOMElementBinding extends Binding implements MultiNodeBinding {
private final String myTagName;
public JDOMElementBinding(@NotNull Accessor accessor) {
@@ -68,13 +67,8 @@ class JDOMElementBinding extends Binding {
@Override
public Object deserializeList(Object context, @NotNull List<?> nodes) {
if (myAccessor.getValueClass().isArray()) {
List<Element> result = new SmartList<Element>();
for (Object aNode : nodes) {
if (!XmlSerializerImpl.isIgnoredNode(aNode)) {
result.add((Element)aNode);
}
}
myAccessor.write(context, result.toArray(new Element[nodes.size()]));
//noinspection SuspiciousToArrayCall
myAccessor.write(context, nodes.toArray(new Element[nodes.size()]));
}
else {
Element element = null;
@@ -90,6 +84,11 @@ class JDOMElementBinding extends Binding {
return context;
}
@Override
public boolean isMulti() {
return true;
}
@Override
@Nullable
public Object deserialize(Object context, @NotNull Object node) {
@@ -32,11 +32,11 @@ import java.util.*;
import static com.intellij.util.xmlb.Constants.*;
class MapBinding extends Binding {
class MapBinding extends Binding implements MultiNodeBinding {
private static final Logger LOG = Logger.getInstance(MapBinding.class);
private static final Comparator<Object> KEY_COMPARATOR = new Comparator<Object>() {
@SuppressWarnings("unchecked")
@SuppressWarnings({"unchecked", "NullableProblems"})
@Override
public int compare(Object o1, Object o2) {
if (o1 instanceof Comparable && o2 instanceof Comparable) {
@@ -64,6 +64,11 @@ class MapBinding extends Binding {
myMapAnnotation = accessor.getAnnotation(MapAnnotation.class);
}
@Override
public boolean isMulti() {
return true;
}
@Nullable
@Override
public Object serialize(Object o, @Nullable Object context, SerializationFilter filter) {
@@ -186,7 +191,7 @@ class MapBinding extends Binding {
}
}
else {
k = myKeyBinding.deserializeList(context, entry.getChild(getKeyAttributeName()).getContent());
k = Binding.deserializeList(myKeyBinding, context, XmlSerializerImpl.getFilteredContent(entry.getChild(getKeyAttributeName())));
}
}
@@ -204,7 +209,7 @@ class MapBinding extends Binding {
}
}
else {
v = myValueBinding.deserializeList(context, entry.getChild(getValueAttributeName()).getContent());
v = Binding.deserializeList(myValueBinding, context, XmlSerializerImpl.getFilteredContent(entry.getChild(getValueAttributeName())));
}
}
@@ -0,0 +1,28 @@
/*
* 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 org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
interface MultiNodeBinding {
@Nullable
Object deserializeList(Object context, @NotNull List<?> nodes);
boolean isMulti();
}
@@ -13,13 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.util.xmlb;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.JDOMUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.SmartList;
import com.intellij.util.xmlb.annotations.OptionTag;
import org.jdom.Attribute;
import org.jdom.Content;
@@ -32,8 +29,6 @@ import org.jetbrains.annotations.Nullable;
import java.util.List;
class OptionTagBinding extends BasePrimitiveBinding {
private static final Logger LOG = Logger.getInstance(OptionTagBinding.class);
private final String myTagName;
private final String myNameAttribute;
private final String myValueAttribute;
@@ -91,16 +86,6 @@ class OptionTagBinding extends BasePrimitiveBinding {
return targetElement;
}
@Nullable
@Override
public Object deserializeList(Object context, @NotNull List<?> nodes) {
if (nodes.size() > 1) {
LOG.info("Duplicate options for " + context + " will be ignored");
}
assert !nodes.isEmpty() : "Empty nodes passed to: " + this;
return deserialize(context, ((Element)nodes.get(0)));
}
@Override
public Object deserialize(Object context, @NotNull Object node) {
return deserialize(context, (Element)node);
@@ -108,7 +93,17 @@ class OptionTagBinding extends BasePrimitiveBinding {
private Object deserialize(Object context, Element element) {
Attribute valueAttribute = element.getAttribute(myValueAttribute);
if (valueAttribute != null) {
if (valueAttribute == null) {
List<Content> children = XmlSerializerImpl.getFilteredContent(element);
if (children.isEmpty()) {
myAccessor.write(context, null);
}
else {
assert myBinding != null;
myAccessor.write(context, Binding.deserializeList(myBinding, myAccessor.read(context), children));
}
}
else {
Object value;
if (myConverter != null) {
value = myConverter.fromString(valueAttribute.getValue());
@@ -119,23 +114,6 @@ class OptionTagBinding extends BasePrimitiveBinding {
}
myAccessor.write(context, value);
}
else {
List<Object> children = new SmartList<Object>();
for (Content child : element.getContent()) {
if (!XmlSerializerImpl.isIgnoredNode(child)) {
children.add(child);
}
}
if (!children.isEmpty()) {
assert myBinding != null;
Object value = myBinding.deserializeList(myAccessor.read(context), children);
myAccessor.write(context, value);
}
else {
myAccessor.write(context, null);
}
}
return context;
}
@@ -23,7 +23,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.List;
class PrimitiveValueBinding extends Binding {
class PrimitiveValueBinding extends Binding implements MultiNodeBinding {
private final Class<?> myType;
public PrimitiveValueBinding(@NotNull Class<?> myType, @Nullable Accessor accessor) {
@@ -55,6 +55,11 @@ class PrimitiveValueBinding extends Binding {
}
}
@Override
public boolean isMulti() {
return true;
}
@Override
@Nullable
public Object deserialize(Object o, @NotNull Object node) {
@@ -1,19 +0,0 @@
package com.intellij.util.xmlb;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
abstract class SingleBinding extends Binding {
protected SingleBinding(Accessor accessor) {
super(accessor);
}
@Nullable
@Override
public Object deserializeList(Object context, @NotNull List<?> nodes) {
assert nodes.size() == 1;
return deserialize(context, nodes.get(0));
}
}
@@ -28,7 +28,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.List;
class TagBinding extends BasePrimitiveBinding {
class TagBinding extends BasePrimitiveBinding implements MultiNodeBinding {
private final String myTextIfEmpty;
public TagBinding(@NotNull Accessor accessor, @NotNull Tag tagAnnotation) {
@@ -64,17 +64,22 @@ class TagBinding extends BasePrimitiveBinding {
Element element = (Element)node;
assert element.getName().equals(name);
//noinspection unchecked
children.addAll(((List)(isBeanBinding ? element.getChildren() : element.getContent())));
children.addAll(((List)(isBeanBinding ? element.getChildren() : XmlSerializerImpl.getFilteredContent(element))));
}
return deserialize(context, children, isBeanBinding);
}
@Override
public boolean isMulti() {
return myBinding instanceof MultiNodeBinding && ((MultiNodeBinding)myBinding).isMulti();
}
@Override
@Nullable
public Object deserialize(Object context, @NotNull Object node) {
boolean isBeanBinding = myBinding instanceof BeanBinding;
Element element = (Element)node;
return deserialize(context, isBeanBinding ? element.getChildren() : element.getContent(), isBeanBinding);
return deserialize(context, isBeanBinding ? element.getChildren() : XmlSerializerImpl.getFilteredContent(element), isBeanBinding);
}
private Object deserialize(Object o, List<? extends Content> children, boolean isBeanBinding) {
@@ -87,7 +92,7 @@ class TagBinding extends BasePrimitiveBinding {
children = Collections.<Content>singletonList(new Text(myTextIfEmpty));
}
Object v = myBinding.deserializeList(myAccessor.read(o), children);
Object v = Binding.deserializeList(myBinding, myAccessor.read(o), children);
myAccessor.write(o, XmlSerializerImpl.convert(v, myAccessor.getValueClass()));
}
return o;
@@ -22,15 +22,15 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
//todo: merge with option tag binding
class TagBindingWrapper extends SingleBinding {
private final Binding binding;
class TagBindingWrapper extends Binding {
private final Binding myBinding;
private final String myTagName;
private final String myAttributeName;
public TagBindingWrapper(@NotNull Binding binding, final String tagName, final String attributeName) {
super(binding.myAccessor);
this.binding = binding;
myBinding = binding;
//noinspection unchecked
assert binding.getBoundNodeType().isAssignableFrom(Text.class);
@@ -42,7 +42,7 @@ class TagBindingWrapper extends SingleBinding {
@Override
public Object serialize(Object o, @Nullable Object context, SerializationFilter filter) {
Element e = new Element(myTagName);
Content content = (Content)binding.serialize(o, e, filter);
Content content = (Content)myBinding.serialize(o, e, filter);
if (content != null) {
if (!myAttributeName.isEmpty()) {
e.setAttribute(myAttributeName, content.getValue());
@@ -61,10 +61,10 @@ class TagBindingWrapper extends SingleBinding {
public Object deserialize(Object context, @NotNull Object node) {
Element element = (Element)node;
if (myAttributeName.isEmpty()) {
return binding.deserializeList(context, element.getContent());
return Binding.deserializeList(myBinding, context, XmlSerializerImpl.getFilteredContent(element));
}
else {
return binding.deserialize(context, element.getAttribute(myAttributeName));
return myBinding.deserialize(context, element.getAttribute(myAttributeName));
}
}
@@ -20,7 +20,7 @@ import org.jdom.Text;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class TextBinding extends SingleBinding {
public class TextBinding extends Binding {
private volatile Binding myBinding;
public TextBinding(@NotNull Accessor accessor) {
@@ -17,10 +17,8 @@ package com.intellij.util.xmlb;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import org.jdom.Attribute;
import org.jdom.Comment;
import org.jdom.Element;
import org.jdom.Text;
import org.jdom.*;
import org.jdom.filter.Filter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -29,17 +27,36 @@ import java.lang.ref.SoftReference;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Collection;
import java.util.Date;
import java.util.Map;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author mike
*/
class XmlSerializerImpl {
private static final Filter<Content> CONTENT_FILTER = new Filter<Content>() {
@Override
public boolean matches(Object obj) {
return !isIgnoredNode(obj);
}
};
private static SoftReference<Map<Pair<Type, Accessor>, Binding>> ourBindings;
@NotNull
static List<Content> getFilteredContent(@NotNull Element element) {
List<Content> content = element.getContent();
if (content.isEmpty()) {
return content;
}
else if (content.size() == 1) {
return isIgnoredNode(content.get(0)) ? Collections.<Content>emptyList() : content;
}
else {
return element.getContent(CONTENT_FILTER);
}
}
@NotNull
static Element serialize(@NotNull Object object, @NotNull SerializationFilter filter) throws XmlSerializationException {
try {