Support for base64 images in .orc; performance improvement to the JAXBContext
This commit is contained in:
parent
df7ab36b5a
commit
45dae98850
@ -1,14 +1,6 @@
|
|||||||
|
|
||||||
package net.sf.openrocket.preset.xml;
|
package net.sf.openrocket.preset.xml;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
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.XmlValue;
|
|
||||||
|
|
||||||
import net.sf.openrocket.database.Databases;
|
import net.sf.openrocket.database.Databases;
|
||||||
import net.sf.openrocket.material.Material;
|
import net.sf.openrocket.material.Material;
|
||||||
import net.sf.openrocket.motor.Manufacturer;
|
import net.sf.openrocket.motor.Manufacturer;
|
||||||
@ -17,6 +9,22 @@ import net.sf.openrocket.preset.InvalidComponentPresetException;
|
|||||||
import net.sf.openrocket.preset.TypedPropertyMap;
|
import net.sf.openrocket.preset.TypedPropertyMap;
|
||||||
import net.sf.openrocket.unit.UnitGroup;
|
import net.sf.openrocket.unit.UnitGroup;
|
||||||
|
|
||||||
|
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 java.awt.image.BufferedImage;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for the external representation of all component presets.
|
* Base class for the external representation of all component presets.
|
||||||
*/
|
*/
|
||||||
@ -35,6 +43,10 @@ public abstract class BaseComponentDTO {
|
|||||||
private AnnotatedMassDTO mass;
|
private AnnotatedMassDTO mass;
|
||||||
@XmlElement(name="Filled")
|
@XmlElement(name="Filled")
|
||||||
private Boolean filled;
|
private Boolean filled;
|
||||||
|
@XmlInlineBinaryData
|
||||||
|
@XmlJavaTypeAdapter(Base64Adapter.class)
|
||||||
|
@XmlElement(name = "Thumbnail")
|
||||||
|
private byte[] image;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default constructor.
|
* Default constructor.
|
||||||
@ -118,7 +130,30 @@ public abstract class BaseComponentDTO {
|
|||||||
this.filled = filled;
|
this.filled = filled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract ComponentPreset asComponentPreset(List<MaterialDTO> materials) throws InvalidComponentPresetException;
|
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) {
|
void addProps(TypedPropertyMap props, List<MaterialDTO> materialList) {
|
||||||
props.put(ComponentPreset.MANUFACTURER, Manufacturer.getManufacturer(manufacturer));
|
props.put(ComponentPreset.MANUFACTURER, Manufacturer.getManufacturer(manufacturer));
|
||||||
@ -162,15 +197,15 @@ public abstract class BaseComponentDTO {
|
|||||||
material = theMaterial.getName();
|
material = theMaterial.getName();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class AnnotatedLengthDTO {
|
static class AnnotatedLengthDTO {
|
||||||
@XmlAttribute(name="Unit", required=false)
|
@XmlAttribute(name="Unit", required=false)
|
||||||
private String unitName = "m";
|
private String unitName = "m";
|
||||||
@XmlValue
|
@XmlValue
|
||||||
private double length;
|
private double length;
|
||||||
|
|
||||||
AnnotatedLengthDTO() {}
|
AnnotatedLengthDTO() {}
|
||||||
|
|
||||||
AnnotatedLengthDTO( double length ) {
|
AnnotatedLengthDTO( double length ) {
|
||||||
this.length = length;
|
this.length = length;
|
||||||
}
|
}
|
||||||
@ -179,21 +214,37 @@ public abstract class BaseComponentDTO {
|
|||||||
return UnitGroup.UNITS_LENGTH.getUnit(unitName).fromUnit(length);
|
return UnitGroup.UNITS_LENGTH.getUnit(unitName).fromUnit(length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class AnnotatedMassDTO {
|
static class AnnotatedMassDTO {
|
||||||
@XmlAttribute(name="Unit", required=false)
|
@XmlAttribute(name="Unit", required=false)
|
||||||
private String unitName = "kg";
|
private String unitName = "kg";
|
||||||
@XmlValue
|
@XmlValue
|
||||||
private double mass;
|
private double mass;
|
||||||
|
|
||||||
AnnotatedMassDTO() {}
|
AnnotatedMassDTO() {}
|
||||||
|
|
||||||
AnnotatedMassDTO( double mass ) {
|
AnnotatedMassDTO( double mass ) {
|
||||||
this.mass = mass;
|
this.mass = mass;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getValue() {
|
public double getValue() {
|
||||||
return UnitGroup.UNITS_MASS.getUnit(unitName).fromUnit(mass);
|
return UnitGroup.UNITS_MASS.getUnit(unitName).fromUnit(mass);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class Base64Adapter extends XmlAdapter<String, byte[]> {
|
||||||
|
public byte[] unmarshal(String s) {
|
||||||
|
if (s == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return DatatypeConverter.parseBase64Binary(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String marshal(byte[] bytes) {
|
||||||
|
if (bytes == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return DatatypeConverter.printBase64Binary(bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package net.sf.openrocket.preset.xml;
|
|||||||
import net.sf.openrocket.material.Material;
|
import net.sf.openrocket.material.Material;
|
||||||
import net.sf.openrocket.preset.ComponentPreset;
|
import net.sf.openrocket.preset.ComponentPreset;
|
||||||
import net.sf.openrocket.preset.InvalidComponentPresetException;
|
import net.sf.openrocket.preset.InvalidComponentPresetException;
|
||||||
|
import net.sf.openrocket.startup.Application;
|
||||||
|
|
||||||
import javax.xml.bind.JAXBContext;
|
import javax.xml.bind.JAXBContext;
|
||||||
import javax.xml.bind.JAXBException;
|
import javax.xml.bind.JAXBException;
|
||||||
@ -21,6 +22,20 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public class OpenRocketComponentSaver {
|
public class OpenRocketComponentSaver {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The JAXBContext. JAXBContext is thread-safe.
|
||||||
|
*/
|
||||||
|
private static JAXBContext context = null;
|
||||||
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
context = JAXBContext.newInstance(OpenRocketComponentDTO.class);
|
||||||
|
}
|
||||||
|
catch (JAXBException jaxb) {
|
||||||
|
Application.getLogger().error("Unable to create JAXBContext for loading of *.orc files.", jaxb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method marshals a list of materials and ComponentPresets into an .orc formatted XML string.
|
* This method marshals a list of materials and ComponentPresets into an .orc formatted XML string.
|
||||||
*
|
*
|
||||||
@ -33,9 +48,8 @@ public class OpenRocketComponentSaver {
|
|||||||
*/
|
*/
|
||||||
public String marshalToOpenRocketComponent(List<Material> theMaterialList, List<ComponentPreset> thePresetList) throws
|
public String marshalToOpenRocketComponent(List<Material> theMaterialList, List<ComponentPreset> thePresetList) throws
|
||||||
JAXBException {
|
JAXBException {
|
||||||
|
/** The context is thread-safe, but marshallers are not. Create a local one. */
|
||||||
JAXBContext binder = JAXBContext.newInstance(OpenRocketComponentDTO.class);
|
Marshaller marshaller = context.createMarshaller();
|
||||||
Marshaller marshaller = binder.createMarshaller();
|
|
||||||
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
|
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
|
||||||
StringWriter sw = new StringWriter();
|
StringWriter sw = new StringWriter();
|
||||||
|
|
||||||
@ -88,8 +102,8 @@ public class OpenRocketComponentSaver {
|
|||||||
* was in an invalid format
|
* was in an invalid format
|
||||||
*/
|
*/
|
||||||
private OpenRocketComponentDTO fromOpenRocketComponent(Reader is) throws JAXBException {
|
private OpenRocketComponentDTO fromOpenRocketComponent(Reader is) throws JAXBException {
|
||||||
JAXBContext bind = JAXBContext.newInstance(OpenRocketComponentDTO.class);
|
/** The context is thread-safe, but unmarshallers are not. Create a local one. */
|
||||||
Unmarshaller unmarshaller = bind.createUnmarshaller();
|
Unmarshaller unmarshaller = context.createUnmarshaller();
|
||||||
return (OpenRocketComponentDTO) unmarshaller.unmarshal(is);
|
return (OpenRocketComponentDTO) unmarshaller.unmarshal(is);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,72 @@
|
|||||||
|
package net.sf.openrocket.preset.xml;
|
||||||
|
|
||||||
|
import net.sf.openrocket.motor.Manufacturer;
|
||||||
|
import net.sf.openrocket.preset.ComponentPreset;
|
||||||
|
import net.sf.openrocket.preset.ComponentPresetFactory;
|
||||||
|
import net.sf.openrocket.preset.TypedPropertyMap;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import javax.xml.bind.JAXBContext;
|
||||||
|
import javax.xml.bind.Marshaller;
|
||||||
|
import javax.xml.bind.Unmarshaller;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.awt.image.DataBufferByte;
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public class BaseComponentDTOTest {
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testImage() throws Exception {
|
||||||
|
TypedPropertyMap presetspec = new TypedPropertyMap();
|
||||||
|
presetspec.put(ComponentPreset.TYPE, ComponentPreset.Type.BODY_TUBE);
|
||||||
|
presetspec.put(ComponentPreset.MANUFACTURER, Manufacturer.getManufacturer("manufacturer"));
|
||||||
|
presetspec.put(ComponentPreset.PARTNO, "partno");
|
||||||
|
presetspec.put(ComponentPreset.LENGTH, 2.0);
|
||||||
|
presetspec.put(ComponentPreset.OUTER_DIAMETER, 2.0);
|
||||||
|
presetspec.put(ComponentPreset.INNER_DIAMETER, 1.0);
|
||||||
|
presetspec.put(ComponentPreset.MASS, 100.0);
|
||||||
|
ComponentPreset preset = ComponentPresetFactory.create(presetspec);
|
||||||
|
|
||||||
|
//Convert the presets to a BodyTubeDTO
|
||||||
|
BodyTubeDTO dto = new BodyTubeDTO(preset);
|
||||||
|
|
||||||
|
//Add an image to the DTO.
|
||||||
|
BufferedImage image = ImageIO.read(this.getClass().getResourceAsStream("or_launcher.png"));
|
||||||
|
dto.setImage(image);
|
||||||
|
|
||||||
|
JAXBContext binder = JAXBContext.newInstance(OpenRocketComponentDTO.class);
|
||||||
|
Marshaller marshaller = binder.createMarshaller();
|
||||||
|
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
|
||||||
|
StringWriter sw = new StringWriter();
|
||||||
|
|
||||||
|
//Serialize the dto to XML
|
||||||
|
marshaller.marshal(dto, sw);
|
||||||
|
String xml = sw.toString();
|
||||||
|
|
||||||
|
//Read the XML back to create the dto again
|
||||||
|
Unmarshaller unmarshaller = binder.createUnmarshaller();
|
||||||
|
BodyTubeDTO redone = (BodyTubeDTO) unmarshaller.unmarshal(new StringReader(xml));
|
||||||
|
|
||||||
|
//Compare the image.
|
||||||
|
Assert.assertArrayEquals(((DataBufferByte) image.getData().getDataBuffer()).getData(),
|
||||||
|
((DataBufferByte) redone.getImage().getData().getDataBuffer()).getData());
|
||||||
|
|
||||||
|
//Assert the rest of the attributes.
|
||||||
|
Assert.assertEquals(dto.getInsideDiameter(), redone.getInsideDiameter(), 0.00001);
|
||||||
|
Assert.assertEquals(dto.getLength(), redone.getLength(), 0.00001);
|
||||||
|
Assert.assertEquals(dto.getOutsideDiameter(), redone.getOutsideDiameter(), 0.00001);
|
||||||
|
Assert.assertEquals(dto.getDescription(), redone.getDescription());
|
||||||
|
Assert.assertEquals(dto.getManufacturer(), redone.getManufacturer());
|
||||||
|
Assert.assertEquals(dto.getMass(), redone.getMass(), 0.00001);
|
||||||
|
Assert.assertEquals(dto.getPartNo(), redone.getPartNo());
|
||||||
|
|
||||||
|
//Uncomment if you want to write the image to a file to view it.
|
||||||
|
// ImageIO.write(redone.getImage(), "png", new FileOutputStream("redone.png"));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,112 @@
|
|||||||
|
package net.sf.openrocket.preset.xml;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OpenRocketComponentSaver Tester.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class OpenRocketComponentSaverTest {
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void before() throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void after() throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Method: marshalToOpenRocketComponent(List<Material> theMaterialList, List<ComponentPreset> thePresetList)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testMarshalToOpenRocketComponent() throws Exception {
|
||||||
|
//TODO: Test goes here...
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Method: unmarshalFromOpenRocketComponent(Reader is)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testUnmarshalFromOpenRocketComponent() throws Exception {
|
||||||
|
//TODO: Test goes here...
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Method: save(OutputStream dest, List<Material> theMaterialList, List<ComponentPreset> thePresetList)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testSave() throws Exception {
|
||||||
|
//TODO: Test goes here...
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Method: fromOpenRocketComponent(Reader is)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testFromOpenRocketComponent() throws Exception {
|
||||||
|
//TODO: Test goes here...
|
||||||
|
/*
|
||||||
|
try {
|
||||||
|
Method method = OpenRocketComponentSaver.getClass().getMethod("fromOpenRocketComponent", Reader.class);
|
||||||
|
method.setAccessible(true);
|
||||||
|
method.invoke(<Object>, <Parameters>);
|
||||||
|
} catch(NoSuchMethodException e) {
|
||||||
|
} catch(IllegalAccessException e) {
|
||||||
|
} catch(InvocationTargetException e) {
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Method: toOpenRocketComponentDTO(List<Material> theMaterialList, List<ComponentPreset> thePresetList)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testToOpenRocketComponentDTO() throws Exception {
|
||||||
|
//TODO: Test goes here...
|
||||||
|
/*
|
||||||
|
try {
|
||||||
|
Method method = OpenRocketComponentSaver.getClass().getMethod("toOpenRocketComponentDTO", List<Material>.class, List<ComponentPreset>.class);
|
||||||
|
method.setAccessible(true);
|
||||||
|
method.invoke(<Object>, <Parameters>);
|
||||||
|
} catch(NoSuchMethodException e) {
|
||||||
|
} catch(IllegalAccessException e) {
|
||||||
|
} catch(InvocationTargetException e) {
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Method: toComponentDTO(ComponentPreset thePreset)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testToComponentDTO() throws Exception {
|
||||||
|
//TODO: Test goes here...
|
||||||
|
/*
|
||||||
|
try {
|
||||||
|
Method method = OpenRocketComponentSaver.getClass().getMethod("toComponentDTO", ComponentPreset.class);
|
||||||
|
method.setAccessible(true);
|
||||||
|
method.invoke(<Object>, <Parameters>);
|
||||||
|
} catch(NoSuchMethodException e) {
|
||||||
|
} catch(IllegalAccessException e) {
|
||||||
|
} catch(InvocationTargetException e) {
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user