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;
|
||||
|
||||
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.material.Material;
|
||||
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.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.
|
||||
*/
|
||||
@ -35,6 +43,10 @@ public abstract class BaseComponentDTO {
|
||||
private AnnotatedMassDTO mass;
|
||||
@XmlElement(name="Filled")
|
||||
private Boolean filled;
|
||||
@XmlInlineBinaryData
|
||||
@XmlJavaTypeAdapter(Base64Adapter.class)
|
||||
@XmlElement(name = "Thumbnail")
|
||||
private byte[] image;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
@ -118,7 +130,30 @@ public abstract class BaseComponentDTO {
|
||||
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) {
|
||||
props.put(ComponentPreset.MANUFACTURER, Manufacturer.getManufacturer(manufacturer));
|
||||
@ -162,15 +197,15 @@ public abstract class BaseComponentDTO {
|
||||
material = theMaterial.getName();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static class AnnotatedLengthDTO {
|
||||
@XmlAttribute(name="Unit", required=false)
|
||||
private String unitName = "m";
|
||||
@XmlValue
|
||||
private double length;
|
||||
|
||||
|
||||
AnnotatedLengthDTO() {}
|
||||
|
||||
|
||||
AnnotatedLengthDTO( double length ) {
|
||||
this.length = length;
|
||||
}
|
||||
@ -179,21 +214,37 @@ public abstract class BaseComponentDTO {
|
||||
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[]> {
|
||||
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.preset.ComponentPreset;
|
||||
import net.sf.openrocket.preset.InvalidComponentPresetException;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
|
||||
import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.JAXBException;
|
||||
@ -21,6 +22,20 @@ import java.util.List;
|
||||
*/
|
||||
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.
|
||||
*
|
||||
@ -33,9 +48,8 @@ public class OpenRocketComponentSaver {
|
||||
*/
|
||||
public String marshalToOpenRocketComponent(List<Material> theMaterialList, List<ComponentPreset> thePresetList) throws
|
||||
JAXBException {
|
||||
|
||||
JAXBContext binder = JAXBContext.newInstance(OpenRocketComponentDTO.class);
|
||||
Marshaller marshaller = binder.createMarshaller();
|
||||
/** The context is thread-safe, but marshallers are not. Create a local one. */
|
||||
Marshaller marshaller = context.createMarshaller();
|
||||
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
|
||||
StringWriter sw = new StringWriter();
|
||||
|
||||
@ -88,8 +102,8 @@ public class OpenRocketComponentSaver {
|
||||
* was in an invalid format
|
||||
*/
|
||||
private OpenRocketComponentDTO fromOpenRocketComponent(Reader is) throws JAXBException {
|
||||
JAXBContext bind = JAXBContext.newInstance(OpenRocketComponentDTO.class);
|
||||
Unmarshaller unmarshaller = bind.createUnmarshaller();
|
||||
/** The context is thread-safe, but unmarshallers are not. Create a local one. */
|
||||
Unmarshaller unmarshaller = context.createUnmarshaller();
|
||||
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