/* * Copyright 2000-2009 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.ReflectionCache; 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 ((modifiers & Modifier.PUBLIC) == 0 || (modifiers & Modifier.STATIC) != 0) 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 (ReflectionCache.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 (ReflectionCache.isInstance(value, JDOMExternalizable.class)) { 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)) { try { field.setInt(data, Integer.parseInt(value)); } catch (NumberFormatException ex) { throw new InvalidDataException(); } } 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)) { if (value != null) { try { int rgb = Integer.parseInt(value, 16); field.set(data, new Color(rgb)); } catch (NumberFormatException ex) { LOG.debug("Wrong color value: " + value, ex); throw new InvalidDataException(); } } else { field.set(data, null); } } else if (ReflectionCache.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(); } } } }