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