Merge branch 'master' of git.labs.intellij.net:idea/community

This commit is contained in:
Michael Golubev
2014-01-07 16:12:42 +01:00
13 changed files with 310 additions and 196 deletions
@@ -956,7 +956,7 @@ public class CommonCodeStyleSettings {
public void serialize(Element indentOptionsElement, final IndentOptions defaultOptions) {
XmlSerializer.serializeInto(this, indentOptionsElement, new SkipDefaultValuesSerializationFilters() {
@Override
protected void configure(Object o) {
protected void configure(@NotNull Object o) {
if (o instanceof IndentOptions && defaultOptions != null) {
((IndentOptions)o).copyFrom(defaultOptions);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* 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.
@@ -22,43 +22,50 @@ import org.jdom.Text;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class AttributeBinding implements Binding {
private final Accessor myAccessor;
private final Attribute myAttribute;
private Binding myBinding;
public AttributeBinding(final Accessor accessor, final Attribute attribute) {
myAccessor = accessor;
myAttribute = attribute;
public class AttributeBinding extends BasePrimitiveBinding {
public AttributeBinding(@NotNull Accessor accessor, @NotNull Attribute attribute) {
super(accessor, attribute.value(), attribute.converter());
}
@Override
public Object serialize(@NotNull Object o, Object context, SerializationFilter filter) {
final Object v = myAccessor.read(o);
if (v == null) return context;
Object value = myAccessor.read(o);
if (value == null) {
return context;
}
final Object node = myBinding.serialize(v, context, filter);
return new org.jdom.Attribute(myAttribute.value(), ((Content)node).getValue());
String stringValue;
if (myConverter != null) {
stringValue = myConverter.toString(value);
}
else {
assert myBinding != null;
stringValue = ((Content)myBinding.serialize(value, context, filter)).getValue();
}
return new org.jdom.Attribute(myName, stringValue);
}
@Override
@Nullable
public Object deserialize(Object context, @NotNull Object... nodes) {
assert nodes.length == 1;
Object node = nodes[0];
org.jdom.Attribute node = (org.jdom.Attribute)nodes[0];
assert isBoundTo(node);
org.jdom.Attribute attr = (org.jdom.Attribute)node;
final String value = attr.getValue();
final Text text = new Text(value);
myAccessor.write(context, myBinding.deserialize(context, text));
Object value;
if (myConverter != null) {
value = myConverter.fromString(node.getValue());
}
else {
assert myBinding != null;
value = myBinding.deserialize(context, new Text(node.getValue()));
}
myAccessor.write(context, value);
return context;
}
@Override
public boolean isBoundTo(Object node) {
return node instanceof org.jdom.Attribute && ((org.jdom.Attribute)node).getName().equals(myAttribute.value());
return node instanceof org.jdom.Attribute && ((org.jdom.Attribute)node).getName().equals(myName);
}
@Override
@@ -68,8 +75,8 @@ public class AttributeBinding implements Binding {
@Override
public void init() {
myBinding = XmlSerializerImpl.getBinding(myAccessor);
if (!Text.class.isAssignableFrom(myBinding.getBoundNodeType())) {
super.init();
if (myBinding != null && !Text.class.isAssignableFrom(myBinding.getBoundNodeType())) {
throw new XmlSerializationException("Can't use attribute binding for non-text content: " + myAccessor);
}
}
@@ -0,0 +1,47 @@
/*
* 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.text.StringUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
abstract class BasePrimitiveBinding implements Binding {
protected final Accessor myAccessor;
protected final String myName;
protected final @Nullable Converter<Object> myConverter;
@Nullable protected Binding myBinding;
protected BasePrimitiveBinding(@NotNull Accessor accessor, @Nullable String suggestedName, @Nullable Class<? extends Converter> converterClass) {
myAccessor = accessor;
myName = StringUtil.isEmpty(suggestedName) ? myAccessor.getName() : suggestedName;
if (converterClass == null || converterClass == Converter.class) {
myConverter = null;
}
else {
//noinspection unchecked
myConverter = XmlSerializerImpl.newInstance(converterClass);
}
}
@Override
public void init() {
if (myConverter == null) {
myBinding = XmlSerializerImpl.getBinding(myAccessor);
}
}
}
@@ -169,18 +169,7 @@ class BeanBinding implements Binding {
}
private Object instantiateBean() {
Object result;
try {
result = myBeanClass.newInstance();
}
catch (InstantiationException e) {
throw new XmlSerializationException(e);
}
catch (IllegalAccessException e) {
throw new XmlSerializationException(e);
}
return result;
return XmlSerializerImpl.newInstance(myBeanClass);
}
@Override
@@ -0,0 +1,27 @@
/*
* 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;
public abstract class Converter<T> {
@Nullable
public abstract T fromString(@NotNull String value);
@NotNull
public abstract String toString(@NotNull T t);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
* 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.
@@ -20,6 +20,7 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.JDOMUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.SmartList;
import com.intellij.util.xmlb.annotations.OptionTag;
import org.jdom.Attribute;
import org.jdom.Content;
@@ -29,31 +30,24 @@ import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
class OptionTagBinding implements Binding {
private static final Logger LOG = Logger.getInstance("#" + OptionTagBinding.class.getName());
class OptionTagBinding extends BasePrimitiveBinding {
private static final Logger LOG = Logger.getInstance(OptionTagBinding.class);
private final Accessor accessor;
private final String myName;
private final Binding myBinding;
private final String myTagName;
private final String myNameAttribute;
private final String myValueAttribute;
public OptionTagBinding(Accessor accessor, @Nullable OptionTag optionTag) {
this.accessor = accessor;
myBinding = XmlSerializerImpl.getBinding(accessor);
public OptionTagBinding(@NotNull Accessor accessor, @Nullable OptionTag optionTag) {
super(accessor, optionTag == null ? null : optionTag.value(), optionTag == null ? null : optionTag.converter());
if (optionTag != null) {
String name = optionTag.value();
myName = name.isEmpty() ? accessor.getName() : name;
myTagName = optionTag.tag();
myNameAttribute = optionTag.nameAttribute();
myValueAttribute = optionTag.valueAttribute();
}
else {
myName = accessor.getName();
myTagName = Constants.OPTION;
myNameAttribute = Constants.NAME;
myValueAttribute = Constants.VALUE;
@@ -63,21 +57,27 @@ class OptionTagBinding implements Binding {
@Override
public Object serialize(Object o, Object context, SerializationFilter filter) {
Element targetElement = new Element(myTagName);
Object value = accessor.read(o);
Object value = myAccessor.read(o);
if (!StringUtil.isEmpty(myNameAttribute)) {
targetElement.setAttribute(myNameAttribute, myName);
}
if (value == null) return targetElement;
if (value == null) {
return targetElement;
}
Object node = myBinding.serialize(value, targetElement, filter);
if (node instanceof Text) {
Text text = (Text)node;
targetElement.setAttribute(myValueAttribute, text.getText());
if (myConverter != null) {
targetElement.setAttribute(myValueAttribute, myConverter.toString(value));
}
else {
if (targetElement != node) {
assert myBinding != null;
Object node = myBinding.serialize(value, targetElement, filter);
if (node instanceof Text) {
Text text = (Text)node;
targetElement.setAttribute(myValueAttribute, text.getText());
}
else if (targetElement != node) {
JDOMUtil.addContent(targetElement, node);
}
}
@@ -86,38 +86,45 @@ class OptionTagBinding implements Binding {
}
@Override
public Object deserialize(Object o, @NotNull Object... nodes) {
public Object deserialize(Object context, @NotNull Object... nodes) {
if (nodes.length > 1) {
LOG.info("Duplicate options for " + o + " will be ignored");
LOG.info("Duplicate options for " + context + " will be ignored");
}
assert nodes.length != 0 : "Empty nodes passed to: " + this;
Element element = ((Element)nodes[0]);
Attribute valueAttr = element.getAttribute(myValueAttribute);
Attribute valueAttribute = element.getAttribute(myValueAttribute);
if (valueAttr != null) {
Object value = myBinding.deserialize(o, valueAttr);
accessor.write(o, value);
if (valueAttribute != null) {
Object value;
if (myConverter != null) {
value = myConverter.fromString(valueAttribute.getValue());
}
else {
assert myBinding != null;
value = myBinding.deserialize(context, valueAttribute);
}
myAccessor.write(context, value);
}
else {
final Content[] childElements = JDOMUtil.getContent(element);
List<Object> children = new ArrayList<Object>();
for (final Content child : childElements) {
if (XmlSerializerImpl.isIgnoredNode(child)) continue;
children.add(child);
List<Object> children = new SmartList<Object>();
for (Content child : element.getContent()) {
if (!XmlSerializerImpl.isIgnoredNode(child)) {
children.add(child);
}
}
if (!children.isEmpty()) {
Object value = myBinding.deserialize(accessor.read(o), ArrayUtil.toObjectArray(children));
accessor.write(o, value);
assert myBinding != null;
Object value = myBinding.deserialize(myAccessor.read(context), ArrayUtil.toObjectArray(children));
myAccessor.write(context, value);
}
else {
accessor.write(o, null);
myAccessor.write(context, null);
}
}
return o;
return context;
}
@Override
@@ -137,11 +144,6 @@ class OptionTagBinding implements Binding {
throw new UnsupportedOperationException("Method getBoundNodeType is not supported in " + getClass());
}
@Override
public void init() {
}
@NonNls
public String toString() {
return "OptionTagBinding[" + myName + ", binding=" + myBinding + "]";
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* 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.
@@ -18,16 +18,18 @@ package com.intellij.util.xmlb;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.JDOMUtil;
import gnu.trove.THashMap;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
public class SkipDefaultValuesSerializationFilters implements SerializationFilter {
private final Map<Class, Object> myDefaultBeans = new HashMap<Class, Object>();
private final Map<Class<?>, Object> myDefaultBeans = new THashMap<Class<?>, Object>();
@Override
public boolean accepts(final Accessor accessor, final Object bean) {
public boolean accepts(final Accessor accessor, @Nullable Object bean) {
if (bean == null) {
return true;
}
@@ -36,35 +38,28 @@ public class SkipDefaultValuesSerializationFilters implements SerializationFilte
final Object defValue = accessor.read(defaultBean);
final Object beanValue = accessor.read(bean);
if (defValue instanceof Element && beanValue instanceof Element) {
return !JDOMUtil.writeElement((Element)defValue, "\n").equals(JDOMUtil.writeElement((Element)beanValue, "\n"));
return !JDOMUtil.areElementsEqual((Element)beanValue, (Element)defValue);
}
else {
return !Comparing.equal(beanValue, defValue);
}
return !Comparing.equal(beanValue, defValue);
}
private Object getDefaultBean(final Object bean) {
Class c = bean.getClass();
private Object getDefaultBean(@NotNull Object bean) {
Class<?> c = bean.getClass();
Object o = myDefaultBeans.get(c);
if (o == null) {
try {
o = c.newInstance();
configure(o);
}
catch (InstantiationException e) {
throw new XmlSerializationException(e);
}
catch (IllegalAccessException e) {
throw new XmlSerializationException(e);
}
o = XmlSerializerImpl.newInstance(c);
configure(o);
myDefaultBeans.put(c, o);
}
return o;
}
protected void configure(final Object o) {
//todo put your own default object configuration here
/**
* Override to put your own default object configuration
*/
protected void configure(@NotNull Object o) {
}
}
@@ -22,6 +22,7 @@ import org.jetbrains.annotations.Nullable;
import java.lang.annotation.Annotation;
import java.lang.ref.SoftReference;
import java.lang.reflect.Constructor;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
@@ -179,4 +180,23 @@ class XmlSerializerImpl {
return result.toArray(new Content[result.size()]);
}
/**
* {@link Class#newInstance()} cannot instantiate private classes
*/
static <T> T newInstance(Class<T> aClass) {
try {
Constructor<T> constructor = aClass.getDeclaredConstructor();
try {
constructor.setAccessible(true);
}
catch (SecurityException e) {
return aClass.newInstance();
}
return constructor.newInstance();
}
catch (Exception e) {
throw new XmlSerializationException(e);
}
}
}
@@ -16,6 +16,8 @@
package com.intellij.util.xmlb.annotations;
import com.intellij.util.xmlb.Converter;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -24,5 +26,7 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE, ElementType.METHOD})
public @interface Attribute {
String value();
String value() default "";
Class<? extends Converter> converter() default Converter.class;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
* 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.
@@ -16,6 +16,7 @@
package com.intellij.util.xmlb.annotations;
import com.intellij.util.xmlb.Constants;
import com.intellij.util.xmlb.Converter;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
@@ -32,7 +33,12 @@ import java.lang.annotation.Target;
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface OptionTag {
String value() default "";
String tag() default Constants.OPTION;
String nameAttribute() default Constants.NAME;
String valueAttribute() default Constants.VALUE;
Class<? extends Converter> converter() default Converter.class;
}
@@ -17,13 +17,16 @@
package com.intellij.util.xmlb;
import com.intellij.openapi.util.JDOMUtil;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.xmlb.annotations.*;
import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.Collection;
import java.util.concurrent.atomic.AtomicReference;
/**
@@ -1086,6 +1089,60 @@ public class XmlSerializerTest extends TestCase {
bean);
}
private static class BeanWithConverter {
private static class MyConverter extends Converter<Ref<String>> {
@Nullable
@Override
public Ref<String> fromString(@NotNull String value) {
return Ref.create(value);
}
@NotNull
@Override
public String toString(@NotNull Ref<String> o) {
return StringUtil.notNullize(o.get());
}
}
@Attribute(converter = MyConverter.class)
public Ref<String> foo;
@OptionTag(converter = MyConverter.class)
public Ref<String> bar;
}
public void testConverter() {
BeanWithConverter bean = new BeanWithConverter();
doSerializerTest("<BeanWithConverter>\n" +
" <option name=\"bar\" />\n" +
"</BeanWithConverter>", bean);
bean.foo = Ref.create("testValue");
doSerializerTest("<BeanWithConverter foo=\"testValue\">\n" +
" <option name=\"bar\" />\n" +
"</BeanWithConverter>", bean);
bean.foo = Ref.create();
bean.bar = Ref.create("testValue2");
doSerializerTest("<BeanWithConverter foo=\"\">\n" +
" <option name=\"bar\" value=\"testValue2\" />\n" +
"</BeanWithConverter>", bean);
}
private static class BeanWithDefaultAttributeName {
@Attribute
public String getFoo() {
return "foo";
}
public void setFoo(@SuppressWarnings("UnusedParameters") String value) {
}
}
public void testDefaultAttributeName() {
BeanWithDefaultAttributeName bean = new BeanWithDefaultAttributeName();
doSerializerTest("<BeanWithDefaultAttributeName foo=\"foo\" />", bean);
}
//---------------------------------------------------------------------------------------------------
private static void assertSerializer(Object bean, String expected, SerializationFilter filter) {
@@ -1,128 +1,73 @@
package com.intellij.ide.browsers;
import com.intellij.openapi.util.text.StringUtil;
import org.jdom.Attribute;
import com.intellij.util.xmlb.SkipDefaultValuesSerializationFilters;
import com.intellij.util.xmlb.XmlSerializer;
import com.intellij.util.xmlb.annotations.Attribute;
import com.intellij.util.xmlb.annotations.Tag;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Sergey Simonchik
*/
@Tag("browser")
public class StartBrowserSettings {
private static final String BROWSER_ELEMENT = "browser";
private static final String START_BROWSER_ATTR = "start";
private static final String BROWSER_NAME_ATTR = "name";
private static final String URL_ATTR = "url";
private static final String WITH_JS_DEBUGGER_ATTR = "with-js-debugger";
private boolean mySelected;
private WebBrowser myBrowser;
private final boolean mySelected;
private final WebBrowser myBrowser;
private final String myUrl;
private final boolean myStartJavaScriptDebugger;
private StartBrowserSettings(boolean selected,
@Nullable WebBrowser browser,
@NotNull String url,
boolean startJavaScriptDebugger) {
mySelected = selected;
myBrowser = browser;
myUrl = url;
myStartJavaScriptDebugger = startJavaScriptDebugger;
}
private String myUrl;
private boolean myStartJavaScriptDebugger;
@Attribute("start")
public boolean isSelected() {
return mySelected;
}
public void setSelected(boolean selected) {
mySelected = selected;
}
@Nullable
@Attribute(value = "name", converter = WebBrowser.ReferenceConverter.class)
public WebBrowser getBrowser() {
return myBrowser;
}
public void setBrowser(@Nullable WebBrowser value) {
myBrowser = value;
}
@Nullable
@Attribute("url")
public String getUrl() {
return myUrl;
}
public void setUrl(@Nullable String value) {
myUrl = value;
}
@Attribute("with-js-debugger")
public boolean isStartJavaScriptDebugger() {
return myStartJavaScriptDebugger;
}
public void setStartJavaScriptDebugger(boolean value) {
myStartJavaScriptDebugger = value;
}
@NotNull
public static StartBrowserSettings readExternal(@NotNull Element parent) {
Builder builder = new Builder();
Element child = parent.getChild(BROWSER_ELEMENT);
if (child != null) {
builder.setSelected(Boolean.parseBoolean(getAttrValue(child, START_BROWSER_ATTR)));
builder.setBrowser(WebBrowserManager.getInstance().findBrowserById(getAttrValue(child, BROWSER_NAME_ATTR)));
builder.setUrl(StringUtil.notNullize(getAttrValue(child, URL_ATTR)));
builder.setStartJavaScriptDebugger(Boolean.parseBoolean(getAttrValue(child, WITH_JS_DEBUGGER_ATTR)));
Element state = parent.getChild("browser");
StartBrowserSettings settings = new StartBrowserSettings();
if (state != null) {
XmlSerializer.deserializeInto(settings, state);
}
return builder.build();
return settings;
}
public void writeExternal(@NotNull Element parent) {
Element child = new Element(BROWSER_ELEMENT);
child.setAttribute(START_BROWSER_ATTR, String.valueOf(isSelected()));
if (myBrowser != null) {
child.setAttribute(BROWSER_NAME_ATTR, myBrowser.getId().toString());
}
child.setAttribute(URL_ATTR, getUrl());
child.setAttribute(WITH_JS_DEBUGGER_ATTR, String.valueOf(isStartJavaScriptDebugger()));
parent.addContent(child);
}
@Nullable
private static String getAttrValue(Element element, String attrKey) {
Attribute attribute = element.getAttribute(attrKey);
return attribute != null ? attribute.getValue() : null;
}
public static class Builder {
private boolean mySelected;
private WebBrowser myBrowser;
private String myUrl = "";
private boolean myStartJavaScriptDebugger;
public Builder() {
}
public Builder(@Nullable StartBrowserSettings settings) {
if (settings != null) {
mySelected = settings.isSelected();
myBrowser = settings.getBrowser();
myUrl = settings.getUrl();
myStartJavaScriptDebugger = settings.isStartJavaScriptDebugger();
}
}
public Builder setSelected(boolean selected) {
mySelected = selected;
return this;
}
/**
* @param browser {@link com.intellij.ide.browsers.WebBrowser} instance,
* null if Default browser needed
*/
public Builder setBrowser(@Nullable WebBrowser browser) {
myBrowser = browser;
return this;
}
public Builder setUrl(@NotNull String url) {
myUrl = url;
return this;
}
public Builder setStartJavaScriptDebugger(boolean startJavaScriptDebugger) {
myStartJavaScriptDebugger = startJavaScriptDebugger;
return this;
}
@NotNull
public StartBrowserSettings build() {
return new StartBrowserSettings(mySelected, myBrowser, myUrl, myStartJavaScriptDebugger);
Element state = XmlSerializer.serialize(this, new SkipDefaultValuesSerializationFilters());
if (!state.getAttributes().isEmpty() || !state.getContent().isEmpty()) {
parent.addContent(state);
}
}
}
@@ -18,6 +18,7 @@ package com.intellij.ide.browsers;
import com.intellij.CommonBundle;
import com.intellij.ide.IdeBundle;
import com.intellij.openapi.util.NullableComputable;
import com.intellij.util.xmlb.Converter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -77,4 +78,18 @@ public abstract class WebBrowser {
public String toString() {
return getName() + " (" + getPath() + ")";
}
public static final class ReferenceConverter extends Converter<WebBrowser> {
@Nullable
@Override
public WebBrowser fromString(@NotNull String value) {
return WebBrowserManager.getInstance().findBrowserById(value);
}
@NotNull
@Override
public String toString(@NotNull WebBrowser browser) {
return browser.getId().toString();
}
}
}