--- /dev/null
+package net.sf.openrocket.preset.xml;
+
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.List;
+
+import javax.imageio.ImageIO;
+import javax.xml.bind.DatatypeConverter;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlInlineBinaryData;
+import javax.xml.bind.annotation.XmlValue;
+import javax.xml.bind.annotation.adapters.XmlAdapter;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+
+import net.sf.openrocket.database.Databases;
+import net.sf.openrocket.material.Material;
+import net.sf.openrocket.motor.Manufacturer;
+import net.sf.openrocket.preset.ComponentPreset;
+import net.sf.openrocket.preset.InvalidComponentPresetException;
+import net.sf.openrocket.preset.TypedPropertyMap;
+import net.sf.openrocket.unit.UnitGroup;
+
+/**
+ * Base class for the external representation of all component presets.
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+public abstract class BaseComponentDTO {
+
+ @XmlElement(name = "Manufacturer")
+ private String manufacturer;
+ @XmlElement(name = "PartNumber")
+ private String partNo;
+ @XmlElement(name = "Description")
+ private String description;
+ @XmlElement(name = "Material")
+ private AnnotatedMaterialDTO material;
+ @XmlElement(name = "Mass")
+ private AnnotatedMassDTO mass;
+ @XmlElement(name = "Filled")
+ private Boolean filled;
+ @XmlInlineBinaryData
+ @XmlJavaTypeAdapter(Base64Adapter.class)
+ @XmlElement(name = "Thumbnail")
+ private byte[] image;
+
+ /**
+ * Default constructor.
+ */
+ protected BaseComponentDTO() {
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param preset the preset to use to pull data values out of
+ *
+ * @throws net.sf.openrocket.util.BugException thrown if the expected body tube keys are not in the preset
+ */
+ protected BaseComponentDTO(final ComponentPreset preset) {
+ setManufacturer(preset.getManufacturer().getSimpleName());
+ setPartNo(preset.getPartNo());
+ if (preset.has(ComponentPreset.DESCRIPTION)) {
+ setDescription(preset.get(ComponentPreset.DESCRIPTION));
+ }
+ if (preset.has(ComponentPreset.MATERIAL)) {
+ setMaterial(new AnnotatedMaterialDTO(preset.get(ComponentPreset.MATERIAL)));
+ }
+ if (preset.has(ComponentPreset.MASS)) {
+ setMass(preset.get(ComponentPreset.MASS));
+ }
+ if (preset.has(ComponentPreset.FILLED)) {
+ setFilled(preset.get(ComponentPreset.FILLED));
+ }
+ if (preset.has(ComponentPreset.IMAGE)) {
+ setImageData(preset.get(ComponentPreset.IMAGE));
+ }
+ }
+
+ public String getManufacturer() {
+ return manufacturer;
+ }
+
+ public void setManufacturer(final String theManufacturer) {
+ manufacturer = theManufacturer;
+ }
+
+ public String getPartNo() {
+ return partNo;
+ }
+
+ public void setPartNo(final String thePartNo) {
+ partNo = thePartNo;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(final String theDescription) {
+ description = theDescription;
+ }
+
+ public AnnotatedMaterialDTO getMaterial() {
+ return material;
+ }
+
+ public void setMaterial(final AnnotatedMaterialDTO theMaterial) {
+ material = theMaterial;
+ }
+
+ public double getMass() {
+ return mass.getValue();
+ }
+
+ public void setMass(final AnnotatedMassDTO theMass) {
+ mass = theMass;
+ }
+
+ public void setMass(final double theMass) {
+ mass = new AnnotatedMassDTO(theMass);
+ }
+
+ public Boolean getFilled() {
+ return filled;
+ }
+
+ public void setFilled(Boolean filled) {
+ this.filled = filled;
+ }
+
+ public byte[] getImageData() {
+ return image;
+ }
+
+ public void setImageData(final byte[] theImage) {
+ image = theImage;
+ }
+
+ public BufferedImage getImage() throws IOException {
+ if (image != null) {
+ return ImageIO.read(new ByteArrayInputStream(image));
+ }
+ return null;
+ }
+
+ public void setImage(BufferedImage theImage) throws IOException {
+ if (theImage != null) {
+ final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ ImageIO.write(theImage, "png", byteArrayOutputStream);
+ image = byteArrayOutputStream.toByteArray();
+ }
+ }
+
+ public abstract ComponentPreset asComponentPreset(List<MaterialDTO> materials) throws InvalidComponentPresetException;
+
+ void addProps(TypedPropertyMap props, List<MaterialDTO> materialList) {
+ props.put(ComponentPreset.MANUFACTURER, Manufacturer.getManufacturer(manufacturer));
+ props.put(ComponentPreset.PARTNO, partNo);
+ if (description != null) {
+ props.put(ComponentPreset.DESCRIPTION, description);
+ }
+ Material m = find(materialList, material);
+ if (m != null) {
+ props.put(ComponentPreset.MATERIAL, find(materialList, material));
+ }
+ if (mass != null) {
+ props.put(ComponentPreset.MASS, getMass());
+ }
+ if (filled != null) {
+ props.put(ComponentPreset.FILLED, getFilled());
+ }
+ if (image != null) {
+ props.put(ComponentPreset.IMAGE, image);
+ }
+ }
+
+ protected Material find(List<MaterialDTO> materialList, AnnotatedMaterialDTO dto) {
+ if (dto == null) {
+ return null;
+ }
+ for (int i = 0; i < materialList.size(); i++) {
+ MaterialDTO materialDTO = materialList.get(i);
+ if (materialDTO.getType().name().equals(dto.type) && materialDTO.getName().equals(dto.material)) {
+ return materialDTO.asMaterial();
+ }
+ }
+
+ // Don't have one, first check OR's database
+ Material m = Databases.findMaterial(dto.getORMaterialType(), dto.material);
+ if (m != null) {
+ return m;
+ }
+
+ return Databases.findMaterial(dto.getORMaterialType(), dto.material, 0.0);
+
+ }
+
+ static class AnnotatedMaterialDTO {
+ @XmlAttribute(name = "Type")
+ private String type;
+ @XmlValue
+ private String material;
+
+ AnnotatedMaterialDTO() {
+ }
+
+ AnnotatedMaterialDTO(Material theMaterial) {
+ type = theMaterial.getType().name();
+ material = theMaterial.getName();
+ }
+
+ public Material.Type getORMaterialType() {
+ if ("BULK".equals(type)) {
+ return Material.Type.BULK;
+ } else if ("SURFACE".equals(type)) {
+ return Material.Type.SURFACE;
+ } else if ("LINE".equals(type)) {
+ return Material.Type.LINE;
+ }
+ throw new IllegalArgumentException("Inavlid material type " + type + " specified for Component");
+ }
+ }
+
+ static class AnnotatedLengthDTO {
+ @XmlAttribute(name = "Unit", required = false)
+ private String unitName = "m";
+ @XmlValue
+ private double length;
+
+ AnnotatedLengthDTO() {
+ }
+
+ AnnotatedLengthDTO(double length) {
+ this.length = length;
+ }
+
+ public double getValue() {
+ return UnitGroup.UNITS_LENGTH.getUnit(unitName).fromUnit(length);
+ }
+ }
+
+ static class AnnotatedMassDTO {
+ @XmlAttribute(name = "Unit", required = false)
+ private String unitName = "kg";
+ @XmlValue
+ private double mass;
+
+ AnnotatedMassDTO() {
+ }
+
+ AnnotatedMassDTO(double mass) {
+ this.mass = mass;
+ }
+
+ public double getValue() {
+ return UnitGroup.UNITS_MASS.getUnit(unitName).fromUnit(mass);
+ }
+ }
+
+ static class Base64Adapter extends XmlAdapter<String, byte[]> {
+ @Override
+ public byte[] unmarshal(String s) {
+ if (s == null) {
+ return null;
+ }
+ return DatatypeConverter.parseBase64Binary(s);
+ }
+
+ @Override
+ public String marshal(byte[] bytes) {
+ if (bytes == null) {
+ return null;
+ }
+ return DatatypeConverter.printBase64Binary(bytes);
+ }
+ }
+}