blob: a5beb43052c9fd225a5c26ac71c3c8d61bc38b5b [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.openapi.util;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.util.ReflectionUtil;
import com.intellij.util.xmlb.annotations.Transient;
import org.jdom.Element;
import org.jdom.Verifier;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.List;
/**
* @deprecated {@link com.intellij.util.xmlb.XmlSerializer} should be used instead
* @author mike
*/
@SuppressWarnings({"HardCodedStringLiteral"})
public class DefaultJDOMExternalizer {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.util.DefaultJDOMExternalizer");
private DefaultJDOMExternalizer() {
}
public interface JDOMFilter{
boolean isAccept(@NotNull Field field);
}
public static void writeExternal(@NotNull Object data, @NotNull Element parentNode) throws WriteExternalException {
writeExternal(data, parentNode, null);
}
public static void writeExternal(@NotNull Object data,
@NotNull Element parentNode,
@Nullable("null means all elements accepted") JDOMFilter filter) throws WriteExternalException {
Field[] fields = data.getClass().getFields();
for (Field field : fields) {
if (field.getName().indexOf('$') >= 0) continue;
int modifiers = field.getModifiers();
if (!(Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers) &&
/*!Modifier.isFinal(modifiers) &&*/ !Modifier.isTransient(modifiers) &&
field.getAnnotation(Transient.class) == null)) continue;
field.setAccessible(true); // class might be non-public
Class type = field.getType();
if (filter != null && !filter.isAccept(field)) {
continue;
}
String value = null;
try {
if (type.isPrimitive()) {
if (type.equals(byte.class)) {
value = Byte.toString(field.getByte(data));
}
else if (type.equals(short.class)) {
value = Short.toString(field.getShort(data));
}
else if (type.equals(int.class)) {
value = Integer.toString(field.getInt(data));
}
else if (type.equals(long.class)) {
value = Long.toString(field.getLong(data));
}
else if (type.equals(float.class)) {
value = Float.toString(field.getFloat(data));
}
else if (type.equals(double.class)) {
value = Double.toString(field.getDouble(data));
}
else if (type.equals(char.class)) {
value = String.valueOf(field.getChar(data));
}
else if (type.equals(boolean.class)) {
value = Boolean.toString(field.getBoolean(data));
}
else {
continue;
}
}
else if (type.equals(String.class)) {
value = filterXMLCharacters((String)field.get(data));
}
else if (type.equals(Color.class)) {
Color color = (Color)field.get(data);
if (color != null) {
value = Integer.toString(color.getRGB() & 0xFFFFFF, 16);
}
}
else if (ReflectionUtil.isAssignable(JDOMExternalizable.class, type)) {
Element element = new Element("option");
parentNode.addContent(element);
element.setAttribute("name", field.getName());
JDOMExternalizable domValue = (JDOMExternalizable)field.get(data);
if (domValue != null) {
Element valueElement = new Element("value");
element.addContent(valueElement);
domValue.writeExternal(valueElement);
}
continue;
}
else {
LOG.debug("Wrong field type: " + type);
continue;
}
}
catch (IllegalAccessException e) {
continue;
}
Element element = new Element("option");
parentNode.addContent(element);
element.setAttribute("name", field.getName());
if (value != null) {
element.setAttribute("value", value);
}
}
}
@Nullable
public static String filterXMLCharacters(String value) {
if (value != null) {
StringBuilder builder = null;
for (int i=0; i<value.length();i++) {
char c = value.charAt(i);
if (Verifier.isXMLCharacter(c)) {
if (builder != null) {
builder.append(c);
}
}
else {
if (builder == null) {
builder = new StringBuilder(value.length()+5);
builder.append(value, 0, i);
}
}
}
if (builder != null) {
value = builder.toString();
}
}
return value;
}
public static void readExternal(@NotNull Object data, Element parentNode) throws InvalidDataException{
if (parentNode == null) return;
for (final Object o : parentNode.getChildren("option")) {
Element e = (Element)o;
String fieldName = e.getAttributeValue("name");
if (fieldName == null) {
throw new InvalidDataException();
}
try {
Field field = data.getClass().getField(fieldName);
Class type = field.getType();
int modifiers = field.getModifiers();
if ((modifiers & Modifier.PUBLIC) == 0 || (modifiers & Modifier.STATIC) != 0) continue;
field.setAccessible(true); // class might be non-public
if ((modifiers & Modifier.FINAL) != 0) {
// read external contents of final field
Object value = field.get(data);
if (JDOMExternalizable.class.isInstance(value)) {
final List children = e.getChildren("value");
for (Object child : children) {
Element valueTag = (Element)child;
((JDOMExternalizable)value).readExternal(valueTag);
}
}
continue;
}
String value = e.getAttributeValue("value");
if (type.isPrimitive()) {
if (value != null) {
if (type.equals(byte.class)) {
try {
field.setByte(data, Byte.parseByte(value));
}
catch (NumberFormatException ex) {
throw new InvalidDataException();
}
}
else if (type.equals(short.class)) {
try {
field.setShort(data, Short.parseShort(value));
}
catch (NumberFormatException ex) {
throw new InvalidDataException();
}
}
else if (type.equals(int.class)) {
int i = toInt(value);
field.setInt(data, i);
}
else if (type.equals(long.class)) {
try {
field.setLong(data, Long.parseLong(value));
}
catch (NumberFormatException ex) {
throw new InvalidDataException();
}
}
else if (type.equals(float.class)) {
try {
field.setFloat(data, Float.parseFloat(value));
}
catch (NumberFormatException ex) {
throw new InvalidDataException();
}
}
else if (type.equals(double.class)) {
try {
field.setDouble(data, Double.parseDouble(value));
}
catch (NumberFormatException ex) {
throw new InvalidDataException();
}
}
else if (type.equals(char.class)) {
if (value.length() != 1) {
throw new InvalidDataException();
}
field.setChar(data, value.charAt(0));
}
else if (type.equals(boolean.class)) {
if (value.equals("true")) {
field.setBoolean(data, true);
}
else if (value.equals("false")) {
field.setBoolean(data, false);
}
else {
throw new InvalidDataException();
}
}
else {
throw new InvalidDataException();
}
}
}
else if (type.equals(String.class)) {
field.set(data, value);
}
else if (type.equals(Color.class)) {
Color color = toColor(value);
field.set(data, color);
}
else if (ReflectionUtil.isAssignable(JDOMExternalizable.class, type)) {
final List children = e.getChildren("value");
if (!children.isEmpty()) {
// compatibility with Selena's serialization which writes an empty tag for a bean which has a default value
JDOMExternalizable object = null;
for (final Object o1 : children) {
Element el = (Element)o1;
object = (JDOMExternalizable)type.newInstance();
object.readExternal(el);
}
field.set(data, object);
}
}
else {
throw new InvalidDataException("wrong type: " + type);
}
}
catch (NoSuchFieldException ex) {
LOG.debug(ex);
}
catch (SecurityException ex) {
throw new InvalidDataException();
}
catch (IllegalAccessException ex) {
throw new InvalidDataException(ex);
}
catch (InstantiationException ex) {
throw new InvalidDataException();
}
}
}
public static int toInt(@NotNull String value) throws InvalidDataException {
int i;
try {
i = Integer.parseInt(value);
}
catch (NumberFormatException ex) {
throw new InvalidDataException(value, ex);
}
return i;
}
public static Color toColor(@Nullable String value) throws InvalidDataException {
Color color;
if (value == null) {
color = null;
}
else {
try {
int rgb = Integer.parseInt(value, 16);
color = new Color(rgb);
}
catch (NumberFormatException ex) {
LOG.debug("Wrong color value: " + value, ex);
throw new InvalidDataException("Wrong color value: " + value, ex);
}
}
return color;
}
}