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 javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.StringWriter;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* The active manager class that is the entry point for reading and writing *.orc files.
*/
public class OpenRocketComponentSaver {
private static final Logger log = LoggerFactory.getLogger(OpenRocketComponentSaver.class);
/**
* The JAXBContext. JAXBContext is thread-safe.
*/
private static JAXBContext context = null;
static {
try {
context = JAXBContext.newInstance(OpenRocketComponentDTO.class);
}
catch (JAXBException jaxb) {
log.error("Unable to create JAXBContext for loading of *.orc files.", jaxb);
}
}
public boolean save(File file, List<Material> theMaterialList, List<ComponentPreset> thePresetList) throws
JAXBException,
IOException {
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8"));
writer.write(marshalToOpenRocketComponent(theMaterialList, thePresetList));
writer.flush();
writer.close();
return true;
}
/**
* This method marshals a list of materials and ComponentPresets into an .orc formatted XML string.
*
* @param theMaterialList the list of materials to be included
* @param thePresetList the list of presets to be included
*
* @return ORC-compliant XML
*
* @throws JAXBException
*/
public String marshalToOpenRocketComponent(List<Material> theMaterialList, List<ComponentPreset> thePresetList) throws
JAXBException {
/** 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();
// We're going to sort the initial data since that makes the output much easier on the eyes.
Collections.sort(theMaterialList, new Comparator<Material>() {
@Override
public int compare(Material o1, Material o2) {
return o1.getName().compareTo( o2.getName() );
}
});
Collections.sort(thePresetList, new Comparator<ComponentPreset>() {
@Override
public int compare(ComponentPreset o1, ComponentPreset o2) {
int manucmp = o1.getManufacturer().getSimpleName().compareTo( o2.getManufacturer().getSimpleName() );
if ( manucmp != 0 ) {
return manucmp;
}
return o1.getPartNo().compareTo( o2.getPartNo());
}
});
marshaller.marshal(toOpenRocketComponentDTO(theMaterialList, thePresetList), sw);
return sw.toString();
}
/**
* This method unmarshals from a Reader that is presumed to be open on an XML file in .orc format.
*
* @param is an open reader; StringBufferInputStream could not be used because it's deprecated and does not handle
* UTF characters correctly
*
* @return a list of ComponentPresets
*
* @throws InvalidComponentPresetException
*
*/
public OpenRocketComponentDTO unmarshalFromOpenRocketComponent(Reader is) throws JAXBException,
InvalidComponentPresetException {
return fromOpenRocketComponent(is);
}
/**
* Write an XML representation of a list of presets.
*
* @param dest the stream to write the data to
* @param theMaterialList the list of materials to be included
* @param thePresetList the list of presets to be included
*
* @throws JAXBException
* @throws IOException thrown if the stream could not be written
*/
public void save(OutputStream dest, List<Material> theMaterialList, List<ComponentPreset> thePresetList) throws
IOException,
JAXBException {
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(dest, "UTF-8"));
writer.write(marshalToOpenRocketComponent(theMaterialList, thePresetList));
writer.flush();
writer.close();
}
/**
* Read from an open Reader instance XML in .orc format and reconstruct an OpenRocketComponentDTO instance.
*
* @param is an open Reader; assumed to be opened on a file of XML in .orc format
*
* @return the OpenRocketComponentDTO that is a POJO representation of the XML; null if the data could not be read
* or was in an invalid format
*/
private OpenRocketComponentDTO fromOpenRocketComponent(Reader is) throws JAXBException {
/** The context is thread-safe, but unmarshallers are not. Create a local one. */
Unmarshaller unmarshaller = context.createUnmarshaller();
return (OpenRocketComponentDTO) unmarshaller.unmarshal(is); //new StreamSource(is));
}
/**
* Root conversion method. It iterates over all subcomponents.
*
* @return a corresponding ORC representation
*/
private OpenRocketComponentDTO toOpenRocketComponentDTO(List<Material> theMaterialList, List<ComponentPreset> thePresetList) {
OpenRocketComponentDTO rsd = new OpenRocketComponentDTO();
if (theMaterialList != null) {
for (Material material : theMaterialList) {
rsd.addMaterial(new MaterialDTO(material));
}
}
if (thePresetList != null) {
for (ComponentPreset componentPreset : thePresetList) {
rsd.addComponent(toComponentDTO(componentPreset));
}
}
return rsd;
}
/**
* Factory method that maps a preset to the corresponding DTO handler.
*
* @param thePreset the preset for which a handler will be found
*
* @return a subclass of BaseComponentDTO that can be used for marshalling/unmarshalling a preset; null if not found
* for the preset type
*/
private static BaseComponentDTO toComponentDTO(ComponentPreset thePreset) {
switch (thePreset.getType()) {
case BODY_TUBE:
return new BodyTubeDTO(thePreset);
case TUBE_COUPLER:
return new TubeCouplerDTO(thePreset);
case NOSE_CONE:
return new NoseConeDTO(thePreset);
case TRANSITION:
return new TransitionDTO(thePreset);
case BULK_HEAD:
return new BulkHeadDTO(thePreset);
case CENTERING_RING:
return new CenteringRingDTO(thePreset);
case ENGINE_BLOCK:
return new EngineBlockDTO(thePreset);
case LAUNCH_LUG:
return new LaunchLugDTO(thePreset);
case STREAMER:
return new StreamerDTO(thePreset);
case PARACHUTE:
return new ParachuteDTO(thePreset);
}
return null;
}
}