diff --git a/diylc/diylc-core/src/main/java/org/diylc/serialization/ISerializationAliasProvider.java b/diylc/diylc-core/src/main/java/org/diylc/serialization/ISerializationAliasProvider.java
new file mode 100644
index 00000000..c522e509
--- /dev/null
+++ b/diylc/diylc-core/src/main/java/org/diylc/serialization/ISerializationAliasProvider.java
@@ -0,0 +1,44 @@
+/*
+
+ DIY Layout Creator (DIYLC).
+ Copyright (c) 2009-2025 held jointly by the individual authors.
+
+ This file is part of DIYLC.
+
+ DIYLC is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ DIYLC is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DIYLC. If not, see .
+
+*/
+package org.diylc.serialization;
+
+import com.thoughtworks.xstream.XStream;
+
+import org.diylc.core.annotations.Indexed;
+
+/**
+ * Registers XStream aliases for classes and fields that were renamed after they shipped, so that
+ * the old names keep resolving. Implementations live next to the classes they alias and are
+ * applied to every {@link XStream} instance prepared by {@link ProjectFileManager#configure(XStream)}.
+ *
+ * @author Branislav Stojkovic
+ */
+@Indexed
+public interface ISerializationAliasProvider {
+
+ /**
+ * Registers aliases on the provided {@link XStream} instance.
+ *
+ * @param xStream
+ */
+ void registerAliases(XStream xStream);
+}
diff --git a/diylc/diylc-core/src/main/java/org/diylc/serialization/ProjectFileManager.java b/diylc/diylc-core/src/main/java/org/diylc/serialization/ProjectFileManager.java
index ac107343..028cf348 100644
--- a/diylc/diylc-core/src/main/java/org/diylc/serialization/ProjectFileManager.java
+++ b/diylc/diylc-core/src/main/java/org/diylc/serialization/ProjectFileManager.java
@@ -133,6 +133,14 @@ public class ProjectFileManager {
xStream.addImmutableType(org.diylc.core.measures.Inductance.class, true);
xStream.addImmutableType(org.diylc.core.measures.Size.class, true);
xStream.addPermission(AnyTypePermission.ANY);
+ try {
+ for (ISerializationAliasProvider provider : ReflectionUtils
+ .getIndexedInstances(ISerializationAliasProvider.class)) {
+ provider.registerAliases(xStream);
+ }
+ } catch (ReflectiveOperationException e) {
+ LOG.error("Could not register serialization aliases", e);
+ }
}
public void startNewFile() {
diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/AbstractLeadedComponent.java b/diylc/diylc-library/src/main/java/org/diylc/components/AbstractLeadedComponent.java
index 373769f2..5701ef43 100644
--- a/diylc/diylc-library/src/main/java/org/diylc/components/AbstractLeadedComponent.java
+++ b/diylc/diylc-library/src/main/java/org/diylc/components/AbstractLeadedComponent.java
@@ -79,7 +79,9 @@ public abstract class AbstractLeadedComponent extends AbstractLabeledComponen
protected Color leadColor = LEAD_COLOR;
protected Display display = Display.NAME;
private boolean flipStanding = false;
- private LabelOriantation labelOriantation = LabelOriantation.Directional;
+ @Deprecated
+ private LabelOrientation labelOriantation;
+ private LabelOrientation labelOrientation = LabelOrientation.Directional;
protected boolean moveLabel = false;
// parameters for adjusting the label control point
@@ -361,13 +363,13 @@ public abstract class AbstractLeadedComponent extends AbstractLabeledComponen
if (getMoveLabel()) {
g2d.setTransform(oldTransform);
g2d.translate(getPoints()[2].getX(), getPoints()[2].getY());
- if (getLabelOriantation() != LabelOriantation.Horizontal) {
+ if (getLabelOrientation() != LabelOrientation.Horizontal) {
g2d.rotate(theta);
}
StringUtils.drawCenteredText(g2d, label, offset, 0, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
g2d.setTransform(oldTransform);
} else {
- if (isStanding() || getLabelOriantation() == LabelOriantation.Horizontal) {
+ if (isStanding() || getLabelOrientation() == LabelOrientation.Horizontal) {
g2d.setTransform(oldTransform);
double x = (getPoints()[0].getX() + getPoints()[1].getX() - length) / 2.0;
double y = (getPoints()[0].getY() + getPoints()[1].getY() - width) / 2.0;
@@ -381,7 +383,7 @@ public abstract class AbstractLeadedComponent extends AbstractLabeledComponen
}
drawingObserver.stopTracking();
}
-// if (getLabelOriantation() == LabelOriantation.Horizontal) {
+// if (getLabelOrientation() == LabelOrientation.Horizontal) {
//
// drawCenteredText(g2d, label, getNewPoints()[2].getX(), getNewPoints()[2].getX(), HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
// g2d.drawString(label, (int) (x + (length - textRect.getWidth()) / 2 + offset),
@@ -698,14 +700,20 @@ public abstract class AbstractLeadedComponent extends AbstractLabeledComponen
}
@EditableProperty(name = "Label Orientation")
- public LabelOriantation getLabelOriantation() {
- if (labelOriantation == null)
- labelOriantation = LabelOriantation.Directional;
- return labelOriantation;
+ public LabelOrientation getLabelOrientation() {
+ if (labelOrientation == null) {
+ if (labelOriantation != null) {
+ labelOrientation = labelOriantation;
+ labelOriantation = null;
+ } else {
+ labelOrientation = LabelOrientation.Directional;
+ }
+ }
+ return labelOrientation;
}
- public void setLabelOriantation(LabelOriantation labelOriantation) {
- this.labelOriantation = labelOriantation;
+ public void setLabelOrientation(LabelOrientation labelOrientation) {
+ this.labelOrientation = labelOrientation;
}
@Override
@@ -785,7 +793,7 @@ public abstract class AbstractLeadedComponent extends AbstractLabeledComponen
this.hideShortLeads = hideShortLeads;
}
- public enum LabelOriantation {
+ public enum LabelOrientation {
Directional, Horizontal
}
}
diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/CopperTrace.java b/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/CopperTrace.java
index b65b1e41..a8a7dfe6 100644
--- a/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/CopperTrace.java
+++ b/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/CopperTrace.java
@@ -187,9 +187,9 @@ public class CopperTrace extends AbstractLeadedComponent implements ILayer
@Deprecated
@Override
- public LabelOriantation getLabelOriantation() {
+ public LabelOrientation getLabelOrientation() {
// TODO Auto-generated method stub
- return super.getLabelOriantation();
+ return super.getLabelOrientation();
}
@Deprecated
diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/Jumper.java b/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/Jumper.java
index 3e2dfbbf..2c9f8d37 100644
--- a/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/Jumper.java
+++ b/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/Jumper.java
@@ -163,9 +163,9 @@ public class Jumper extends AbstractLeadedComponent implements IContinuity
@Deprecated
@Override
- public LabelOriantation getLabelOriantation() {
+ public LabelOrientation getLabelOrientation() {
// TODO Auto-generated method stub
- return super.getLabelOriantation();
+ return super.getLabelOrientation();
}
@Override
diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/Line.java b/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/Line.java
index ad8203fe..1779870d 100644
--- a/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/Line.java
+++ b/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/Line.java
@@ -291,7 +291,7 @@ public class Line extends AbstractLeadedComponent {
@Deprecated
@Override
- public org.diylc.components.AbstractLeadedComponent.LabelOriantation getLabelOriantation() {
- return super.getLabelOriantation();
+ public org.diylc.components.AbstractLeadedComponent.LabelOrientation getLabelOrientation() {
+ return super.getLabelOrientation();
}
}
diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/passive/Resistor.java b/diylc/diylc-library/src/main/java/org/diylc/components/passive/Resistor.java
index c6c9d2aa..2a4a2c11 100644
--- a/diylc/diylc-library/src/main/java/org/diylc/components/passive/Resistor.java
+++ b/diylc/diylc-library/src/main/java/org/diylc/components/passive/Resistor.java
@@ -197,7 +197,7 @@ public class Resistor extends AbstractLeadedComponent implements IGe
@Override
protected int getLabelOffset(int bodyLength, int bodyWidth, int labelLength) {
- if (value == null || getColorCode() == ResistorColorCode.NONE || getLabelOriantation() != AbstractLeadedComponent.LabelOriantation.Directional)
+ if (value == null || getColorCode() == ResistorColorCode.NONE || getLabelOrientation() != AbstractLeadedComponent.LabelOrientation.Directional)
return 0;
Color[] bands = value.getColorCode(colorCode);
diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/semiconductors/BridgeRectifier.java b/diylc/diylc-library/src/main/java/org/diylc/components/semiconductors/BridgeRectifier.java
index e4c8296b..db3a16bf 100644
--- a/diylc/diylc-library/src/main/java/org/diylc/components/semiconductors/BridgeRectifier.java
+++ b/diylc/diylc-library/src/main/java/org/diylc/components/semiconductors/BridgeRectifier.java
@@ -37,7 +37,7 @@ import org.diylc.awt.StringUtils;
import org.diylc.common.*;
import org.diylc.components.AbstractLabeledComponent;
import org.diylc.components.RoundedPolygon;
-import org.diylc.components.AbstractLeadedComponent.LabelOriantation;
+import org.diylc.components.AbstractLeadedComponent.LabelOrientation;
import org.diylc.core.ComponentState;
import org.diylc.core.IDIYComponent;
import org.diylc.core.IDrawingObserver;
@@ -107,7 +107,9 @@ public class BridgeRectifier extends AbstractLabeledComponent implements
private Voltage voltage;
private Current current;
transient private Area[] body;
- private LabelOriantation labelOriantation = LabelOriantation.Directional;
+ @Deprecated
+ private LabelOrientation labelOriantation;
+ private LabelOrientation labelOrientation = LabelOrientation.Directional;
public BridgeRectifier() {
super();
@@ -457,7 +459,7 @@ public class BridgeRectifier extends AbstractLabeledComponent implements
AffineTransform oldTransform = g2d.getTransform();
- if (getLabelOriantation() == LabelOriantation.Directional
+ if (getLabelOrientation() == LabelOrientation.Directional
&& (getOrientation() == Orientation.DEFAULT || getOrientation() == Orientation._180)) {
double centerX = bounds.getX() + bounds.width / 2;
double centerY = bounds.getY() + bounds.height / 2;
@@ -671,14 +673,20 @@ public class BridgeRectifier extends AbstractLabeledComponent implements
}
@EditableProperty(name = "Label Orientation")
- public LabelOriantation getLabelOriantation() {
- if (labelOriantation == null)
- labelOriantation = LabelOriantation.Directional;
- return labelOriantation;
+ public LabelOrientation getLabelOrientation() {
+ if (labelOrientation == null) {
+ if (labelOriantation != null) {
+ labelOrientation = labelOriantation;
+ labelOriantation = null;
+ } else {
+ labelOrientation = LabelOrientation.Directional;
+ }
+ }
+ return labelOrientation;
}
- public void setLabelOriantation(LabelOriantation labelOriantation) {
- this.labelOriantation = labelOriantation;
+ public void setLabelOrientation(LabelOrientation labelOrientation) {
+ this.labelOrientation = labelOrientation;
}
@Override
diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/shapes/TapeMeasure.java b/diylc/diylc-library/src/main/java/org/diylc/components/shapes/TapeMeasure.java
index c0867e68..d91f13fe 100644
--- a/diylc/diylc-library/src/main/java/org/diylc/components/shapes/TapeMeasure.java
+++ b/diylc/diylc-library/src/main/java/org/diylc/components/shapes/TapeMeasure.java
@@ -344,8 +344,8 @@ public class TapeMeasure extends AbstractLeadedComponent {
@Deprecated
@Override
- public org.diylc.components.AbstractLeadedComponent.LabelOriantation getLabelOriantation() {
- return org.diylc.components.AbstractLeadedComponent.LabelOriantation.Directional;
+ public org.diylc.components.AbstractLeadedComponent.LabelOrientation getLabelOrientation() {
+ return org.diylc.components.AbstractLeadedComponent.LabelOrientation.Directional;
}
@Deprecated
diff --git a/diylc/diylc-library/src/main/java/org/diylc/serialization/ComponentAliasProvider.java b/diylc/diylc-library/src/main/java/org/diylc/serialization/ComponentAliasProvider.java
new file mode 100644
index 00000000..ed83a411
--- /dev/null
+++ b/diylc/diylc-library/src/main/java/org/diylc/serialization/ComponentAliasProvider.java
@@ -0,0 +1,41 @@
+/*
+
+ DIY Layout Creator (DIYLC).
+ Copyright (c) 2009-2025 held jointly by the individual authors.
+
+ This file is part of DIYLC.
+
+ DIYLC is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ DIYLC is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DIYLC. If not, see .
+
+*/
+package org.diylc.serialization;
+
+import com.thoughtworks.xstream.XStream;
+
+import org.diylc.components.AbstractLeadedComponent;
+
+public class ComponentAliasProvider implements ISerializationAliasProvider {
+
+ @Override
+ public void registerAliases(XStream xStream) {
+ // the enum shipped misspelled and its class name is saved wherever the value is held as an
+ // Object (variants, defaults in config.xml); configs from before the package alias carry the
+ // full name. The alias registered last is also the one written, so files stay readable by
+ // older builds
+ xStream.alias("org.diylc.components.AbstractLeadedComponent$LabelOriantation",
+ AbstractLeadedComponent.LabelOrientation.class);
+ xStream.alias("diylc.AbstractLeadedComponent$LabelOriantation",
+ AbstractLeadedComponent.LabelOrientation.class);
+ }
+}
diff --git a/diylc/diylc-library/src/test/java/org/diylc/serialization/ComponentAliasProviderTest.java b/diylc/diylc-library/src/test/java/org/diylc/serialization/ComponentAliasProviderTest.java
new file mode 100644
index 00000000..04d09631
--- /dev/null
+++ b/diylc/diylc-library/src/test/java/org/diylc/serialization/ComponentAliasProviderTest.java
@@ -0,0 +1,115 @@
+/*
+
+ DIY Layout Creator (DIYLC).
+ Copyright (c) 2009-2025 held jointly by the individual authors.
+
+ This file is part of DIYLC.
+
+ DIYLC is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ DIYLC is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DIYLC. If not, see .
+
+*/
+package org.diylc.serialization;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.thoughtworks.xstream.XStream;
+import com.thoughtworks.xstream.io.xml.DomDriver;
+
+import org.diylc.components.AbstractLeadedComponent.LabelOrientation;
+import org.diylc.components.passive.Resistor;
+import org.diylc.components.semiconductors.BridgeRectifier;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Files saved before {@code LabelOriantation} was renamed must keep loading. The field is migrated
+ * and saved under its new name; the enum keeps its shipped class name on disk, because older builds
+ * discard a whole config file that names a class they cannot resolve.
+ */
+public class ComponentAliasProviderTest {
+
+ private static final String OLD_ENUM_NAME = "diylc.AbstractLeadedComponent_-LabelOriantation";
+ private static final String OLD_FULL_ENUM_NAME =
+ "org.diylc.components.AbstractLeadedComponent_-LabelOriantation";
+
+ // a fresh instance has not seen any class yet, which is the state it is in while reading
+ // config.xml at startup
+ private static XStream createXStream() {
+ XStream xStream = new XStream(new DomDriver("UTF-8"));
+ ProjectFileManager.configure(xStream);
+ return xStream;
+ }
+
+ // files saved before the rename carry only the misspelled field
+ private static String toOldFieldName(String xml) {
+ Assert.assertTrue(xml.contains("labelOrientation"));
+ return xml.replace("labelOrientation", "labelOriantation");
+ }
+
+ @Test
+ public void leadedComponentMigratesOldFieldName() {
+ Resistor resistor = new Resistor();
+ resistor.setLabelOrientation(LabelOrientation.Horizontal);
+ String oldXml = toOldFieldName(createXStream().toXML(resistor));
+
+ Resistor loaded = (Resistor) createXStream().fromXML(oldXml);
+ Assert.assertEquals(LabelOrientation.Horizontal, loaded.getLabelOrientation());
+
+ String savedXml = createXStream().toXML(loaded);
+ Assert.assertTrue(savedXml.contains("Horizontal"));
+ Assert.assertFalse(savedXml.contains("labelOriantation"));
+ }
+
+ @Test
+ public void bridgeRectifierMigratesOldFieldName() {
+ BridgeRectifier rectifier = new BridgeRectifier();
+ rectifier.setLabelOrientation(LabelOrientation.Horizontal);
+ String oldXml = toOldFieldName(createXStream().toXML(rectifier));
+
+ BridgeRectifier loaded = (BridgeRectifier) createXStream().fromXML(oldXml);
+ Assert.assertEquals(LabelOrientation.Horizontal, loaded.getLabelOrientation());
+
+ String savedXml = createXStream().toXML(loaded);
+ Assert.assertTrue(savedXml.contains("Horizontal"));
+ Assert.assertFalse(savedXml.contains("labelOriantation"));
+ }
+
+ @Test
+ public void enumValueHeldAsObjectReadsOldClassName() {
+ String xml = "";
+
+ Map, ?> loaded = (Map, ?>) createXStream().fromXML(xml);
+ Assert.assertEquals(LabelOrientation.Horizontal, loaded.get("Label Orientation"));
+ }
+
+ @Test
+ public void enumValueHeldAsObjectReadsOldFullClassName() {
+ String xml = "";
+
+ Map, ?> loaded = (Map, ?>) createXStream().fromXML(xml);
+ Assert.assertEquals(LabelOrientation.Horizontal, loaded.get("Label Orientation"));
+ }
+
+ @Test
+ public void enumValueHeldAsObjectWritesOldClassName() {
+ Map values = new HashMap();
+ values.put("Label Orientation", LabelOrientation.Horizontal);
+
+ String xml = createXStream().toXML(values);
+ Assert.assertTrue(xml.contains("<" + OLD_ENUM_NAME + ">Horizontal" + OLD_ENUM_NAME + ">"));
+ }
+}