diff --git a/docking-api/src/ModernDocking/Property.java b/docking-api/src/ModernDocking/Property.java index 48e1379..1b50482 100644 --- a/docking-api/src/ModernDocking/Property.java +++ b/docking-api/src/ModernDocking/Property.java @@ -3,14 +3,26 @@ // TODO might have to expose this to the applications // TODO not sure how we go about converting to the right type without forcing separate calls public abstract class Property { + private final String name; + + public Property(String name) { + + this.name = name; + } + public abstract Class getType(); public abstract boolean isNull(); + public String getName() { + return name; + } + public static class ByteProperty extends Property { private final byte value; - public ByteProperty(byte value) { + public ByteProperty(String name, byte value) { + super(name); this.value = value; } @@ -37,7 +49,8 @@ public byte getValue() { public static class ShortProperty extends Property { private final short value; - public ShortProperty(short value) { + public ShortProperty(String name, short value) { + super(name); this.value = value; } @@ -64,7 +77,8 @@ public short getValue() { public static class IntProperty extends Property { private final int value; - public IntProperty(int value) { + public IntProperty(String name, int value) { + super(name); this.value = value; } @@ -91,7 +105,8 @@ public int getValue() { public static class LongProperty extends Property { private final long value; - public LongProperty(long value) { + public LongProperty(String name, long value) { + super(name); this.value = value; } @@ -118,7 +133,8 @@ public long getValue() { public static class FloatProperty extends Property { private final float value; - public FloatProperty(float value) { + public FloatProperty(String name, float value) { + super(name); this.value = value; } @@ -145,7 +161,8 @@ public float getValue() { public static class DoubleProperty extends Property { private final double value; - public DoubleProperty(double value) { + public DoubleProperty(String name, double value) { + super(name); this.value = value; } @@ -172,7 +189,8 @@ public double getValue() { public static class CharacterProperty extends Property { private final char value; - public CharacterProperty(char value) { + public CharacterProperty(String name, char value) { + super(name); this.value = value; } @@ -199,7 +217,8 @@ public char getValue() { public static class BooleanProperty extends Property { private final boolean value; - public BooleanProperty(boolean value) { + public BooleanProperty(String name, boolean value) { + super(name); this.value = value; } @@ -226,7 +245,8 @@ public boolean getValue() { public static class StringProperty extends Property { private final String value; - public StringProperty(String value) { + public StringProperty(String name, String value) { + super(name); this.value = value; } diff --git a/docking-api/src/ModernDocking/api/LayoutPersistenceAPI.java b/docking-api/src/ModernDocking/api/LayoutPersistenceAPI.java index 57875d8..70f3282 100644 --- a/docking-api/src/ModernDocking/api/LayoutPersistenceAPI.java +++ b/docking-api/src/ModernDocking/api/LayoutPersistenceAPI.java @@ -304,8 +304,9 @@ private void writeSimpleNodeToFile(XMLStreamWriter writer, DockingSimplePanelNod if (value != null && !value.isNull()) { writer.writeStartElement("property"); - writer.writeAttribute("name", value.toString()); - writer.writeAttribute("type", value.getType().toString()); + writer.writeAttribute("name", value.getName()); + writer.writeAttribute("type", value.getType().getSimpleName()); + writer.writeAttribute("value", value.toString()); writer.writeEndElement(); } } @@ -363,8 +364,9 @@ private void writeTabbedNodeToFile(XMLStreamWriter writer, DockingTabPanelNode n if (value != null) { writer.writeStartElement("property"); - writer.writeAttribute("name", value.toString()); - writer.writeAttribute("type", value.getType().toString()); + writer.writeAttribute("name", value.getName()); + writer.writeAttribute("type", value.getType().getSimpleName()); + writer.writeAttribute("value", value.toString()); writer.writeEndElement(); } } @@ -505,39 +507,60 @@ private Map readProperties(XMLStreamReader reader) throws XMLS if (next == XMLStreamConstants.START_ELEMENT) { if (reader.getLocalName().equals("properties")) { - while (!(next == XMLStreamConstants.END_ELEMENT && reader.getLocalName().equals("properties"))) { - readProperty(reader); - next = reader.nextTag(); + // old style of properties from before 0.12.0 + if (reader.getAttributeCount() != 0) { + DockableProperties.setLoadingLegacyFile(true); + + for (int i = 0; i < reader.getAttributeCount(); i++) { + Property prop = DockableProperties.parseProperty(reader.getAttributeLocalName(i), "String", reader.getAttributeValue(i)); + properties.put(String.valueOf(reader.getAttributeName(i)), prop); + } + } + else { + DockableProperties.setLoadingLegacyFile(false); + + while (reader.hasNext()) { + next = reader.nextTag(); + + if (next == XMLStreamConstants.START_ELEMENT && reader.getLocalName().equals("property")) { + String property = null; + String type = null; + String value = null; + + for (int i = 0; i < reader.getAttributeCount(); i++) { + String attributeLocalName = reader.getAttributeLocalName(i); + + switch (attributeLocalName) { + case "name": + property = reader.getAttributeValue(i); + break; + case "type": + type = reader.getAttributeValue(i); + break; + case "value": + value = reader.getAttributeValue(i); + break; + } + } + if (property != null && type != null && value != null) { + Property parsedProperty = DockableProperties.parseProperty(property, type, value); + properties.put(parsedProperty.getName(), parsedProperty); + } + } + else if (next == XMLStreamConstants.END_ELEMENT && reader.getLocalName().equals("properties")) { + break; + } + } } - break; -// for (int i = 0; i < reader.getAttributeCount(); i++) { -// properties.put(String.valueOf(reader.getAttributeName(i)), readProperty(reader)); -// } } } - else if (next == XMLStreamConstants.END_ELEMENT && reader.getLocalName().equals("properties")) { + if (next == XMLStreamConstants.END_ELEMENT && reader.getLocalName().equals("properties")) { break; } } return properties; } - private Property readProperty(XMLStreamReader reader) throws XMLStreamException { - while (reader.hasNext()) { - int next = reader.nextTag(); - - if (next == XMLStreamConstants.START_ELEMENT) { - if (reader.getLocalName().equals("property")) { - // reader.getAttributeValue(i) - } - } - else if (next == XMLStreamConstants.END_ELEMENT && reader.getLocalName().equals("property")) { - break; - } - } - return null; - } - private DockingSplitPanelNode readSplitNodeFromFile(XMLStreamReader reader) throws XMLStreamException { DockingLayoutNode left = null; DockingLayoutNode right = null; @@ -592,15 +615,57 @@ else if (next == XMLStreamConstants.START_ELEMENT && reader.getLocalName().equal else if (next == XMLStreamConstants.START_ELEMENT && reader.getLocalName().equals("properties")) { Map properties = new HashMap<>(); - for (int i = 0; i < reader.getAttributeCount(); i++) { -// properties.put(String.valueOf(reader.getAttributeName(i)), reader.getAttributeValue(i)); + // old style of properties from before 0.12.0 + if (reader.getAttributeCount() != 0) { + DockableProperties.setLoadingLegacyFile(true); + + for (int i = 0; i < reader.getAttributeCount(); i++) { + Property prop = DockableProperties.parseProperty(reader.getAttributeLocalName(i), "String", reader.getAttributeValue(i)); + properties.put(String.valueOf(reader.getAttributeName(i)), prop); + } + } + else { + DockableProperties.setLoadingLegacyFile(false); + + while (reader.hasNext()) { + next = reader.nextTag(); + + if (next == XMLStreamConstants.START_ELEMENT && reader.getLocalName().equals("property")) { + String property = null; + String type = null; + String value = null; + + for (int i = 0; i < reader.getAttributeCount(); i++) { + String attributeLocalName = reader.getAttributeLocalName(i); + + switch (attributeLocalName) { + case "name": + property = reader.getAttributeValue(i); + break; + case "type": + type = reader.getAttributeValue(i); + break; + case "value": + value = reader.getAttributeValue(i); + break; + } + } + if (property != null && type != null && value != null) { + Property parsedProperty = DockableProperties.parseProperty(property, type, value); + properties.put(parsedProperty.getName(), parsedProperty); + } + } + else if (next == XMLStreamConstants.END_ELEMENT && reader.getLocalName().equals("properties")) { + break; + } + } } if (node != null) { node.setProperties(currentPersistentID, properties); } } - else if (next == XMLStreamConstants.END_ELEMENT && reader.getLocalName().equals("tabbed")) { + if (next == XMLStreamConstants.END_ELEMENT && reader.getLocalName().equals("tabbed")) { break; } } diff --git a/docking-api/src/ModernDocking/api/WindowLayoutBuilderAPI.java b/docking-api/src/ModernDocking/api/WindowLayoutBuilderAPI.java index f670e44..fc4b258 100644 --- a/docking-api/src/ModernDocking/api/WindowLayoutBuilderAPI.java +++ b/docking-api/src/ModernDocking/api/WindowLayoutBuilderAPI.java @@ -115,7 +115,7 @@ public WindowLayoutBuilderAPI display(String persistentID) { public WindowLayoutBuilderAPI setProperty(String persistentID, String property, byte value) { Map props = properties.getOrDefault(persistentID, new HashMap<>()); - props.put(property, new Property.ByteProperty(value)); + props.put(property, new Property.ByteProperty(property, value)); properties.put(persistentID, props); @@ -125,7 +125,7 @@ public WindowLayoutBuilderAPI setProperty(String persistentID, String property, public WindowLayoutBuilderAPI setProperty(String persistentID, String property, short value) { Map props = properties.getOrDefault(persistentID, new HashMap<>()); - props.put(property, new Property.ShortProperty(value)); + props.put(property, new Property.ShortProperty(property, value)); properties.put(persistentID, props); @@ -135,7 +135,7 @@ public WindowLayoutBuilderAPI setProperty(String persistentID, String property, public WindowLayoutBuilderAPI setProperty(String persistentID, String property, int value) { Map props = properties.getOrDefault(persistentID, new HashMap<>()); - props.put(property, new Property.IntProperty(value)); + props.put(property, new Property.IntProperty(property, value)); properties.put(persistentID, props); @@ -145,7 +145,7 @@ public WindowLayoutBuilderAPI setProperty(String persistentID, String property, public WindowLayoutBuilderAPI setProperty(String persistentID, String property, long value) { Map props = properties.getOrDefault(persistentID, new HashMap<>()); - props.put(property, new Property.LongProperty(value)); + props.put(property, new Property.LongProperty(property, value)); properties.put(persistentID, props); @@ -155,7 +155,7 @@ public WindowLayoutBuilderAPI setProperty(String persistentID, String property, public WindowLayoutBuilderAPI setProperty(String persistentID, String property, float value) { Map props = properties.getOrDefault(persistentID, new HashMap<>()); - props.put(property, new Property.FloatProperty(value)); + props.put(property, new Property.FloatProperty(property, value)); properties.put(persistentID, props); @@ -165,7 +165,7 @@ public WindowLayoutBuilderAPI setProperty(String persistentID, String property, public WindowLayoutBuilderAPI setProperty(String persistentID, String property, double value) { Map props = properties.getOrDefault(persistentID, new HashMap<>()); - props.put(property, new Property.DoubleProperty(value)); + props.put(property, new Property.DoubleProperty(property, value)); properties.put(persistentID, props); @@ -175,7 +175,7 @@ public WindowLayoutBuilderAPI setProperty(String persistentID, String property, public WindowLayoutBuilderAPI setProperty(String persistentID, String property, char value) { Map props = properties.getOrDefault(persistentID, new HashMap<>()); - props.put(property, new Property.CharacterProperty(value)); + props.put(property, new Property.CharacterProperty(property, value)); properties.put(persistentID, props); @@ -185,7 +185,7 @@ public WindowLayoutBuilderAPI setProperty(String persistentID, String property, public WindowLayoutBuilderAPI setProperty(String persistentID, String property, boolean value) { Map props = properties.getOrDefault(persistentID, new HashMap<>()); - props.put(property, new Property.BooleanProperty(value)); + props.put(property, new Property.BooleanProperty(property, value)); properties.put(persistentID, props); @@ -195,7 +195,7 @@ public WindowLayoutBuilderAPI setProperty(String persistentID, String property, public WindowLayoutBuilderAPI setProperty(String persistentID, String property, String value) { Map props = properties.getOrDefault(persistentID, new HashMap<>()); - props.put(property, new Property.StringProperty(value)); + props.put(property, new Property.StringProperty(property, value)); properties.put(persistentID, props); diff --git a/docking-api/src/ModernDocking/internal/DockableProperties.java b/docking-api/src/ModernDocking/internal/DockableProperties.java index 380a78e..0bf06fc 100644 --- a/docking-api/src/ModernDocking/internal/DockableProperties.java +++ b/docking-api/src/ModernDocking/internal/DockableProperties.java @@ -31,6 +31,12 @@ of this software and associated documentation files (the "Software"), to deal import java.util.stream.Collectors; public class DockableProperties { + private static boolean loadingLegacyFile = false; + + public static void setLoadingLegacyFile(boolean legacy) { + loadingLegacyFile = legacy; + } + public static void configureProperties(DockableWrapper wrapper, Map properties) { Dockable dockable = wrapper.getDockable(); @@ -60,15 +66,21 @@ public static void configureProperties(DockableWrapper wrapper, Map saveProperties(DockableWrapper wrapper) { // grab the property and store the value by its name DockingProperty property = field.getAnnotation(DockingProperty.class); - properties.put(property.name(), getProperty(wrapper, field)); + properties.put(property.name(), getProperty(wrapper, property.name(), field)); } catch (IllegalAccessException ignore) { } @@ -120,49 +132,85 @@ public static void validateProperty(Field field, DockingProperty property) { } public static void validateProperty(Field field, Property property) { - if (createProperty(field, property.toString()).getType() != property.getType()) { + Objects.requireNonNull(field); + Objects.requireNonNull(property); + Property prop = createProperty(field, property.getName(), property.toString()); + if (prop.getType() != property.getType()) { throw new RuntimeException("Type of property does not match type of value."); } } + public static Property parseProperty(String property, String type, String value) { + if (type.equals("byte")) { + return new Property.ByteProperty(property, value.isEmpty() ? (byte) 0 : Byte.parseByte(value)); + } + else if (type.equals("short")) { + return new Property.ShortProperty(property, value.isEmpty() ? (short) 0 : Short.parseShort(value)); + } + else if (type.equals("int")) { + return new Property.IntProperty(property, value.isEmpty() ? 0 : Integer.parseInt(value)); + } + else if (type.equals("long")) { + return new Property.LongProperty(property, value.isEmpty() ? (long) 0 : Long.parseLong(value)); + } + else if (type.equals("float")) { + return new Property.FloatProperty(property, value.isEmpty() ? 0.0f : Float.parseFloat(value)); + } + else if (type.equals("double")) { + return new Property.DoubleProperty(property, value.isEmpty() ? 0.0 : Double.parseDouble(value)); + } + else if (type.equals("char")) { + return new Property.CharacterProperty(property, value.isEmpty() ? '\0' : value.charAt(0)); + } + else if (type.equals("boolean")) { + return new Property.BooleanProperty(property, !value.isEmpty() && Boolean.parseBoolean(value)); + } + else if (type.equals("String")) { + return new Property.StringProperty(property, value); + } + else { + throw new RuntimeException("Unsupported property type"); + } + } + private static Property createProperty(Field field, DockingProperty property) { String value = ""; if (!Objects.equals(property.defaultValue(), "__no_default_value__")) { value = property.defaultValue(); } - return createProperty(field, value); + return createProperty(field, property.name(), value); } - private static Property createProperty(Field field, String value) { + private static Property createProperty(Field field, String property, String value) { Class type = field.getType(); if (type == byte.class) { - return new Property.ByteProperty(value.isEmpty() ? (byte) 0 : Byte.parseByte(value)); + return new Property.ByteProperty(property, value.isEmpty() ? (byte) 0 : Byte.parseByte(value)); } else if (type == short.class) { - return new Property.ShortProperty(value.isEmpty() ? (short) 0 : Short.parseShort(value)); + return new Property.ShortProperty(property, value.isEmpty() ? (short) 0 : Short.parseShort(value)); } else if (type == int.class) { - return new Property.IntProperty(value.isEmpty() ? 0 : Integer.parseInt(value)); + return new Property.IntProperty(property, value.isEmpty() ? 0 : Integer.parseInt(value)); } else if (type == long.class) { - return new Property.LongProperty(value.isEmpty() ? (long) 0 : Long.parseLong(value)); + return new Property.LongProperty(property, value.isEmpty() ? (long) 0 : Long.parseLong(value)); } else if (type == float.class) { - return new Property.FloatProperty(value.isEmpty() ? 0.0f : Float.parseFloat(value)); + return new Property.FloatProperty(property, value.isEmpty() ? 0.0f : Float.parseFloat(value)); } else if (type == double.class) { - return new Property.DoubleProperty(value.isEmpty() ? 0.0 : Double.parseDouble(value)); + return new Property.DoubleProperty(property, value.isEmpty() ? 0.0 : Double.parseDouble(value)); } else if (type == char.class) { - return new Property.CharacterProperty(value.isEmpty() ? '\0' : value.charAt(0)); + return new Property.CharacterProperty(property, value.isEmpty() ? '\0' : value.charAt(0)); } else if (type == boolean.class) { - return new Property.BooleanProperty(!value.isEmpty() && Boolean.parseBoolean(value)); + return new Property.BooleanProperty(property, !value.isEmpty() && Boolean.parseBoolean(value)); } else if (type == String.class) { - return new Property.StringProperty(value); + return new Property.StringProperty(property, value); } // else if (type.isEnum()) { // return Integer.toString(((Enum) field.get(dockable)).ordinal()); @@ -173,37 +221,37 @@ else if (type == String.class) { } } - private static Property getProperty(DockableWrapper wrapper, Field field) throws IllegalAccessException { + private static Property getProperty(DockableWrapper wrapper, String property, Field field) throws IllegalAccessException { Dockable dockable = wrapper.getDockable(); Class type = field.getType(); if (type == byte.class) { - return new Property.ByteProperty((byte) field.get(dockable)); + return new Property.ByteProperty(property, (byte) field.get(dockable)); } else if (type == short.class) { - return new Property.ShortProperty((short) field.get(dockable)); + return new Property.ShortProperty(property, (short) field.get(dockable)); } else if (type == int.class) { - return new Property.IntProperty((int) field.get(dockable)); + return new Property.IntProperty(property, (int) field.get(dockable)); } else if (type == long.class) { - return new Property.LongProperty((long) field.get(dockable)); + return new Property.LongProperty(property, (long) field.get(dockable)); } else if (type == float.class) { - return new Property.FloatProperty((float) field.get(dockable)); + return new Property.FloatProperty(property, (float) field.get(dockable)); } else if (type == double.class) { - return new Property.DoubleProperty((double) field.get(dockable)); + return new Property.DoubleProperty(property, (double) field.get(dockable)); } else if (type == char.class) { - return new Property.CharacterProperty((char) field.get(dockable)); + return new Property.CharacterProperty(property, (char) field.get(dockable)); } else if (type == boolean.class) { - return new Property.BooleanProperty((boolean) field.get(dockable)); + return new Property.BooleanProperty(property, (boolean) field.get(dockable)); } else if (type == String.class) { - return new Property.StringProperty((String) field.get(dockable)); + return new Property.StringProperty(property, (String) field.get(dockable)); } // else if (type.isEnum()) { // return Integer.toString(((Enum) field.get(dockable)).ordinal()); diff --git a/docking-api/src/ModernDocking/layouts/DockingSimplePanelNode.java b/docking-api/src/ModernDocking/layouts/DockingSimplePanelNode.java index a61a477..3a0b46f 100644 --- a/docking-api/src/ModernDocking/layouts/DockingSimplePanelNode.java +++ b/docking-api/src/ModernDocking/layouts/DockingSimplePanelNode.java @@ -28,6 +28,7 @@ of this software and associated documentation files (the "Software"), to deal import ModernDocking.settings.Settings; import javax.swing.*; +import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -156,7 +157,7 @@ public String getClassName() { * @return properties map */ public Map getProperties() { - return properties; + return Collections.unmodifiableMap(properties); } public void setProperties(Map properties) { diff --git a/docking-single-app/src/ModernDocking/app/AppState.java b/docking-single-app/src/ModernDocking/app/AppState.java index 318601b..706b311 100644 --- a/docking-single-app/src/ModernDocking/app/AppState.java +++ b/docking-single-app/src/ModernDocking/app/AppState.java @@ -121,7 +121,7 @@ public static Property getProperty(Dockable dockable, String propertyName) { } public static void setProperty(Dockable dockable, String propertyName, String value) { - instance.setProperty(dockable, propertyName, new Property.StringProperty(value)); + instance.setProperty(dockable, propertyName, new Property.StringProperty(propertyName, value)); } public static void removeProperty(Dockable dockable, String propertyName) {