/*
* @(#)AbstractModelParser.java
*
* Copyright (C) 2006-2010 www.interpss.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This program 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.
*
* @Author Mike Zhou
* @Version 1.0
* @Date 02/01/2010
*
* Revision History
* ================
*
*/
package org.ieee.odm.model;
import static org.ieee.odm.ODMObjectFactory.OdmObjFactory;
import static org.ieee.odm.common.ODMModelContansts.ODM_Schema_NS;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import org.ieee.odm.common.ODMBranchDuplicationException;
import org.ieee.odm.common.ODMException;
import org.ieee.odm.common.ODMLogger;
import org.ieee.odm.model.base.BaseJaxbHelper;
import org.ieee.odm.model.base.ODMModelStringUtil;
import org.ieee.odm.schema.AnalysisCategoryEnumType;
import org.ieee.odm.schema.BaseBranchXmlType;
import org.ieee.odm.schema.BranchXmlType;
import org.ieee.odm.schema.BusIDRefXmlType;
import org.ieee.odm.schema.BusXmlType;
import org.ieee.odm.schema.ContentInfoXmlType;
import org.ieee.odm.schema.IDRecordXmlType;
import org.ieee.odm.schema.LineBranchXmlType;
import org.ieee.odm.schema.ModifyRecordXmlType;
import org.ieee.odm.schema.NetAreaXmlType;
import org.ieee.odm.schema.NetZoneXmlType;
import org.ieee.odm.schema.NetworkCategoryEnumType;
import org.ieee.odm.schema.NetworkXmlType;
import org.ieee.odm.schema.OriginalDataFormatEnumType;
import org.ieee.odm.schema.PSXfrBranchXmlType;
import org.ieee.odm.schema.StudyCaseXmlType;
import org.ieee.odm.schema.StudyScenarioXmlType;
import org.ieee.odm.schema.XfrBranchXmlType;
/**
* Abstract Xml parser implementation as the base for all the IEEE DOM schema parsers.
*
*
*/
public abstract class AbstractModelParser<TNetXml extends NetworkXmlType> implements IODMModelParser {
/** input text file encoding */
protected String encoding = IODMModelParser.DefaultEncoding;
/** bus and branch object cache for fast lookup. */
protected HashMap<String,IDRecordXmlType> objectCache = null;
/** the root StudyCase element */
protected StudyCaseXmlType pssStudyCase = null;
private static Unmarshaller unmarshaller = null;
private static Marshaller marshaller = null;
/**
* Default Constructor
*
*/
public AbstractModelParser() {
this.objectCache = new HashMap<>();
if (!(this instanceof ODMModelParser)) {
this.getStudyCase().setId("ODM_StudyCase");
}
}
/**
* constructor
*
* @param encoding input text encoding
*/
public AbstractModelParser(String encoding) {
this();
this.encoding = encoding;
}
/**
* get encoding
*
* @return
*/
public String getEncoding() { return this.encoding; }
/**
* set encoding
*
* @param e encoding string
*/
public void setEncoding(String e) { this.encoding = e; }
/**
* get object cache lookup table
*
* @return
*/
public HashMap<String,IDRecordXmlType> getObjectCache() { return this.objectCache; }
/**
* parse the xml input file to create a model parser object
*
* @param xmlFile input xml file
*/
@SuppressWarnings("unchecked") public boolean parse(File xmlFile) {
try {
JAXBElement<StudyCaseXmlType> elem = (JAXBElement<StudyCaseXmlType>)createUnmarshaller().unmarshal(xmlFile);
this.pssStudyCase = elem.getValue();
return true;
} catch (JAXBException e) {
e.printStackTrace();
ODMLogger.getLogger().severe(e.toString());
return false;
}
}
/**
* parse the xml string, representing an input xml file, to create a model parser object
*
* @param xmlFile
*/
@SuppressWarnings("unchecked") public boolean parse(String xmlString) {
try {
ByteArrayInputStream bStr = new ByteArrayInputStream(xmlString.getBytes());
JAXBElement<StudyCaseXmlType> elem = (JAXBElement<StudyCaseXmlType>)createUnmarshaller().unmarshal(bStr);
this.pssStudyCase = elem.getValue();
return true;
} catch (JAXBException e) {
e.printStackTrace();
ODMLogger.getLogger().severe(e.toString());
return false;
}
}
/**
* parse the xml file input stream to create a model parser object
*
* @param xmlFile
*/
@SuppressWarnings("unchecked") public boolean parse(InputStream in) {
try {
Object obj = createUnmarshaller().unmarshal(in);
JAXBElement<StudyCaseXmlType> elem = (JAXBElement<StudyCaseXmlType>)obj;
this.pssStudyCase = elem.getValue();
} catch (JAXBException e) {
//e.printStackTrace();
ODMLogger.getLogger().severe("ODM xml doc parsing error, " + e.toString());
return false;
}
// cache the loaded bus and branch objects
for (JAXBElement<? extends BusXmlType> bus : this.getBaseCase().getBusList().getBus()) {
BusXmlType b = bus.getValue();
this.objectCache.put(b.getId(), b);
}
for (JAXBElement<? extends BaseBranchXmlType> branch : this.getBaseCase().getBranchList().getBranch()) {
BaseBranchXmlType b = branch.getValue();
this.objectCache.put(b.getId(), b);
}
return true;
}
/*
* Abstract functions
* ==================
*/
/**
* create BaseCase object, which should be a child of NetworkXmlType, for
* example LoadflowXmlType
*/
protected abstract TNetXml createBaseCase();
/**
* get the base case object
*
* @return
*/
public TNetXml getNet() {
return getBaseCase();
}
/**
* check if the network info stored in the model parser object is for
* transmission network type and loadflow analysis type
*/
public boolean isTransmissionLoadflow() {
return this.getStudyCase().getNetworkCategory() == NetworkCategoryEnumType.TRANSMISSION &&
this.getStudyCase().getAnalysisCategory() == AnalysisCategoryEnumType.LOADFLOW;
}
/*
* Case/Network level functions
* ===========================
*/
/**
* initial case content info, set AnalysisCategory = Loadflow, NetworkCategory = Transmission
*
* @param originalFormat
*/
public void initCaseContentInfo(OriginalDataFormatEnumType originalDataFormat) {
ContentInfoXmlType info = OdmObjFactory.createContentInfoXmlType();
getStudyCase().setContentInfo(info);
info.setOriginalDataFormat(originalDataFormat);
info.setAdapterProviderName("www.interpss.org");
info.setAdapterProviderVersion("1.00");
getStudyCase().setAnalysisCategory(
AnalysisCategoryEnumType.LOADFLOW);
getStudyCase().setNetworkCategory(
NetworkCategoryEnumType.TRANSMISSION);
}
/**
* Set BaseCase content info
*
* @param originalDataFormat
*/
public void setCaseContentInfo(OriginalDataFormatEnumType originalDataFormat) {
ContentInfoXmlType info = OdmObjFactory.createContentInfoXmlType();
getStudyCase().setContentInfo(info);
info.setOriginalDataFormat(originalDataFormat);
info.setAdapterProviderName("www.interpss.org");
info.setAdapterProviderVersion("1.00");
}
/**
* Get the root schema element StudyCase
*
* @return the StudyCase element
*/
public StudyCaseXmlType getStudyCase() {
if (this.pssStudyCase == null) {
this.pssStudyCase = new StudyCaseXmlType();
this.pssStudyCase.setBaseCase(BaseJaxbHelper.network(createBaseCase()));
}
return this.pssStudyCase;
}
/**
* get the base case network object
*
* @return
*/
@SuppressWarnings("unchecked") protected TNetXml getBaseCase() {
return (TNetXml)this.pssStudyCase.getBaseCase().getValue();
}
/**
* get child network list
*
* @return the list
*/
public List<JAXBElement<? extends NetworkXmlType>> getChildNetList() {
return this.pssStudyCase.getChildNet();
}
@Override public ModifyRecordXmlType getModification() {
return this.pssStudyCase.getModificationList() == null? null :
this.pssStudyCase.getModificationList().getModification().get(0);
}
@Override public ModifyRecordXmlType getModification(String id) {
if (this.pssStudyCase.getModificationList() != null)
for (ModifyRecordXmlType mod : this.pssStudyCase.getModificationList().getModification()) {
if (mod.getId().equals(id))
return mod;
}
ODMLogger.getLogger().warning("Modification record not found, id: " + id);
return null;
}
@Override public StudyScenarioXmlType getStudyScenario() {
return this.pssStudyCase.getStudyScenario() == null? null :
this.pssStudyCase.getStudyScenario().getValue();
}
/**
* create an area xml record
*
* @return the area xml record
*/
public NetAreaXmlType createNetworkArea() {
if(getBaseCase().getAreaList() == null){
getBaseCase().setAreaList(OdmObjFactory.createNetworkXmlTypeAreaList());
}
NetAreaXmlType area = OdmObjFactory.createNetAreaXmlType();
getBaseCase().getAreaList().getArea().add(area);
return area;
}
/**
* create a LossZone object
*
* @return the loss zone object
*/
public NetZoneXmlType createNetworkLossZone() {
if(getBaseCase().getLossZoneList() == null){
getBaseCase().setLossZoneList(OdmObjFactory.createNetworkXmlTypeLossZoneList());
}
NetZoneXmlType zone = OdmObjFactory.createNetZoneXmlType();
getBaseCase().getLossZoneList().getLossZone().add(zone);
return zone;
}
/*
* Bus/Branch object, reference functions
* ======================================
*/
/**
* Get the cashed object by id
*
* @param id
* @return
*/
protected IDRecordXmlType getCachedObject(String id) {
return this.objectCache.get(id);
}
/**
* Get the cashed object by id
*
* @param id
*/
protected void removeCachedObject(String id) {
this.objectCache.remove(id);
}
/*
* Bus functions
* =============
*/
/**
* Get the cashed bus object by id
*
* @param id
* @return
*/
@SuppressWarnings("unchecked") public <T extends BusXmlType> T getBus(String id) {
return (T)this.getCachedObject(id);
}
/**
* add a bus object into the BaseCase bus list and to the cashe table
*
* @param bus bus object to be added
*/
public <T extends BusXmlType> void addBus(T bus) {
getBaseCase().getBusList().getBus().add(BaseJaxbHelper.bus(bus));
this.objectCache.put(bus.getId(), bus);
}
/**
* remove a bus object from the cache and BaseCase bus list
*
* @param busId
* @return
*/
public boolean removeBus(String busId) {
this.removeCachedObject(busId);
for (JAXBElement<? extends BusXmlType> busElem : this.getBaseCase().getBusList().getBus()) {
if (busId.equals(busElem.getValue().getId())) {
this.getBaseCase().getBusList().getBus().remove(busElem);
return true;
}
}
return false;
}
/**
* First remove the bus with the busId and then add the bus record
*
* @param busId id of the bus to be removed
* @param bus bus object to be added
*/
public <T extends BusXmlType> void replaceBus(String busId, T bus) {
this.removeBus(busId);
this.addBus(bus);
}
/**
* set bus record id and add the bus object into the cache
*
* @param busRec
* @param id
* @throws Exception
*/
public <T extends BusXmlType> void setBusId(T busRec, String id) throws ODMException {
busRec.setId(id);
if (this.objectCache.get(id) != null) {
throw new ODMException("Bus record duplication, bus id: " + id);
}
this.objectCache.put(id, busRec);
}
/**
* create a ref record with id
*
* @param id
* @return
*/
public BusIDRefXmlType createBusRef(String id) {
BusXmlType rec = this.getBus(id);
if (rec != null) {
BusIDRefXmlType refBus = OdmObjFactory.createBusIDRefXmlType();
refBus.setIdRef(rec);
return refBus;
}
return null;
}
/**
* add a new Bus record to the base case
*
* @return
*/
protected abstract <T extends BusXmlType> T createBus();
/**
* create a bus object with the id, make sure there is no duplication
*
* @param id
* @return
* @throws Exception
*/
public <T extends BusXmlType> T createBus(String id) throws ODMException {
T busRec = createBus();
busRec.setId(id);
if (this.objectCache.get(id) != null) {
throw new ODMException("Bus record duplication, bus id: " + id);
}
this.objectCache.put(id, busRec);
return busRec;
}
/**
* add a new bus record to the base case and to the cache table
*
* @param id
* @param number
* @return
*/
public <T extends BusXmlType> T createBus(String id, long number) throws ODMException {
T busRec = createBus(id);
busRec.setNumber(number);
return busRec;
}
/*
* Branch functions
* ================
*/
/**
* create Line branch object, an abstract method to be implemented by the subclass
*
* @return
*/
protected abstract <T extends LineBranchXmlType> T createLineBranch();
/**
* create Xformer branch object, an abstract method to be implemented by the subclass
*
* @return
*/
protected abstract <T extends XfrBranchXmlType> T createXfrBranch();
/**
* create 3W Xformer branch object, an abstract method to be implemented by the subclass
*
* @return
*/
protected abstract <T extends BaseBranchXmlType> T createXfr3WBranch();
/**
* create PsXformer branch object, an abstract method to be implemented by the subclass
*
* @return
*/
protected abstract <T extends PSXfrBranchXmlType> T createPSXfrBranch();
/**
* create 3W PsXformer branch object, an abstract method to be implemented by the subclass
*
* @return
*/
protected abstract <T extends BaseBranchXmlType> T createPSXfr3WBranch();
/**
* create Line branch object
*
* @param fBusId from bus id
* @param toBusId to bus id
* @param cirId branch circuit number
* @return
* @throws ODMException
* @throws ODMBranchDuplicationException
*/
public <T extends LineBranchXmlType> T createLineBranch(String fBusId,String toBusId, String cirId) throws ODMException, ODMBranchDuplicationException {
T branch = createLineBranch();
intiBranchData(branch);
addBranch2BaseCase(branch, fBusId, toBusId, null, cirId);
return branch;
}
/**
* create Xformer branch object
*
* @param fBusId from bus id
* @param toBusId to bus id
* @param cirId branch circuit number
* @return
* @throws ODMException
* @throws ODMBranchDuplicationException
*/
public <T extends XfrBranchXmlType> T createXfrBranch(String fBusId, String toBusId, String cirId) throws ODMException, ODMBranchDuplicationException {
T branch = createXfrBranch();
intiBranchData(branch);
addBranch2BaseCase(branch, fBusId, toBusId, null, cirId);
return branch;
}
/**
* for creating PSASP Xformer branch object
*
* @param fBusId from bus id
* @param toBusId to bus id
* @param cirId branch circuit number
* @param idName branch name, which is used to be an identifier
* @return
* @throws ODMException
* @throws ODMBranchDuplicationException
*/
public <T extends XfrBranchXmlType> T createXfrBranch(String fBusId, String toBusId, String cirId, String idName) throws ODMException, ODMBranchDuplicationException {
T branch = createXfrBranch();
intiBranchData(branch);
addBranch2BaseCase(branch, fBusId, toBusId, null, cirId, idName);
return branch;
}
/**
* create 3W Xformer branch object
*
* @param fBusId from bus id
* @param toBusId to bus id
* @param terBusId tertiary bus id
* @param cirId branch circuit number
* @return
* @throws ODMException
* @throws ODMBranchDuplicationException
*/
public <T extends BaseBranchXmlType> T createXfr3WBranch(String fBusId,String toBusId, String terBusId, String cirId) throws ODMException, ODMBranchDuplicationException {
T branch = createXfr3WBranch();
intiBranchData(branch);
addBranch2BaseCase(branch, fBusId, toBusId, terBusId, cirId);
return branch;
}
/**
* create PsXformer branch object
*
* @param fBusId from bus id
* @param toBusId to bus id
* @param cirId branch circuit number
* @return
* @throws ODMException
* @throws ODMBranchDuplicationException
*/
public <T extends PSXfrBranchXmlType> T createPSXfrBranch(String fBusId,String toBusId, String cirId) throws ODMException, ODMBranchDuplicationException {
T branch = createPSXfrBranch();
intiBranchData(branch);
addBranch2BaseCase(branch, fBusId, toBusId, null, cirId);
return branch;
}
/**
* create PsXformer branch object
*
* @param fBusId from bus id
* @param toBusId to bus id
* @param cirId branch circuit number
* @param idName branch name, which is used to be an identifier
* @return
* @throws ODMException
* @throws ODMBranchDuplicationException
*/
public <T extends PSXfrBranchXmlType> T createPSXfrBranch(String fBusId,String toBusId, String cirId, String idName) throws ODMException, ODMBranchDuplicationException {
T branch = createPSXfrBranch();
intiBranchData(branch);
addBranch2BaseCase(branch, fBusId, toBusId, null, cirId, idName);
return branch;
}
/**
* Get the cashed branch object by id
*
* @param branchId
* @return
*/
public BaseBranchXmlType getBranch(String branchId) {
return (BaseBranchXmlType)this.getCachedObject(branchId);
}
/**
* remove the branch object from the cache and branch list
*
* @param branchId
* @return
*/
public boolean removeBranch(String branchId) {
this.removeCachedObject(branchId);
for (JAXBElement<? extends BaseBranchXmlType> braElem : this.getBaseCase().getBranchList().getBranch()) {
if (branchId.equals(braElem.getValue().getId())) {
this.getBaseCase().getBranchList().getBranch().remove(braElem);
return true;
}
}
return false;
}
/**
* add a branch object into the branch list and to the cashe table
*
* @param branch
*/
public void addBranch(BranchXmlType branch) {
getBaseCase().getBranchList().getBranch().add(BaseJaxbHelper.branch(branch));
this.objectCache.put(branch.getId(), branch);
}
/**
* get the cashed branch record using fromId, toId and cirId
*
* @param fromId
* @param toId
* @param cirId
* @return
*/
public BaseBranchXmlType getBranch(String fromId, String toId, String cirId) {
String id = ODMModelStringUtil.formBranchId(fromId, toId, cirId);
return this.getBranch(id);
}
/**
* get the cashed branch record using fromId, toId and cirId
*
* @param fromId
* @param toId
* @param tertId
* @param cirId
* @return
*/
public BaseBranchXmlType getBranch(String fromId, String toId, String tertId, String cirId) {
String id = ODMModelStringUtil.formBranchId(fromId, toId, tertId, cirId);
return this.getBranch(id);
}
/**
* First remove the branch with the braId and then add the branch record
*
* @param branchId id of the branch to be removed
* @param branch branch object to be added
*/
public void replaceBranch(String branchId, BranchXmlType branch) {
this.removeBranch(branchId);
this.addBranch(branch);
}
/**
* initialize a BaseBranchXmlType object.
*
* @param branch
*/
protected void intiBranchData(BaseBranchXmlType branch) {
getBaseCase().getBranchList().getBranch().add(BaseJaxbHelper.branch(branch));
branch.setOffLine(false);
branch.setAreaNumber(1);
branch.setZoneNumber(1);
}
/**
* add a BaseBranchXmlType object to the BaseCase
*
* @param branch
* @param fromId
* @param toId
* @param tertId
* @param cirId
* @throws ODMBranchDuplicationException
*/
protected void addBranch2BaseCase(BaseBranchXmlType branch, String fromId, String toId, String tertId, String cirId) throws ODMBranchDuplicationException {
String id = tertId == null ?
ODMModelStringUtil.formBranchId(fromId, toId, cirId) : ODMModelStringUtil.formBranchId(fromId, toId, tertId, cirId);
if (this.objectCache.get(id) != null ||
this.objectCache.get(ODMModelStringUtil.formBranchId(toId, fromId, cirId)) != null) {
throw new ODMBranchDuplicationException("Branch record duplication, bus id: " + id);
}
this.objectCache.put(id, branch);
branch.setCircuitId(cirId);
branch.setId(id);
branch.setFromBus(createBusRef(fromId));
branch.setToBus(createBusRef(toId));
if (tertId != null)
branch.setTertiaryBus(createBusRef(tertId));
}
/**
* There are cases that a Xfr/PsXfr branch needs to be retrieved by its idName. Add a BaseBranchXmlType object to the BaseCase.
* The branch can be by branch id or idName
*
* @param branch
* @param fromId
* @param toId
* @param tertId
* @param cirId
* @param idName branch name, which is used to be an identifier
* @throws ODMBranchDuplicationException
*/
protected void addBranch2BaseCase(BaseBranchXmlType branch, String fromId, String toId, String tertId, String cirId, String idName) throws ODMBranchDuplicationException {
addBranch2BaseCase(branch, fromId, toId, tertId, cirId);
branch.setName(idName);
// add the following item to the hashtable so that Xfr can be retrieved using its idName
this.objectCache.put(idName, branch);
}
/*
* marshall/unmarshall, out functions
* ==================================
*/
/**
* create a Jaxb unmarshaller to unmarshall Xml document to odm object
*
* @return an unmarshaller object
* @throws JAXBException
*/
public Unmarshaller createUnmarshaller() throws JAXBException {
if (unmarshaller == null) {
JAXBContext jaxbContext = JAXBContext.newInstance(ODM_Schema_NS);
unmarshaller = jaxbContext.createUnmarshaller();
//unmarshaller.setProperty(Marshaller.JAXB_ENCODING, "GB18030");
}
return unmarshaller;
}
/**
* create a Jaxb marshaller to marshall the odm object to an Xml document
*
* @return a marshaller object
* @throws JAXBException
*/
public Marshaller createMarshaller() throws JAXBException {
if (marshaller == null) {
JAXBContext jaxbContext = JAXBContext.newInstance(ODM_Schema_NS);
marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
//marshaller.setProperty(Marshaller.JAXB_ENCODING, "GB18030");
marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding);
}
return marshaller;
}
/**
* print the Xml doc to the std out
*
*/
public void stdout() {
try {
JAXBElement<StudyCaseXmlType> element = OdmObjFactory.createPssStudyCase(getStudyCase());
createMarshaller().marshal( element, System.out );
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* convert the model parser to a string
*/
public String toXmlDoc() {
OutputStream ostream = new ByteArrayOutputStream();
try {
JAXBElement<StudyCaseXmlType> element = OdmObjFactory.createPssStudyCase(getStudyCase());
createMarshaller().marshal( element, ostream );
} catch (Exception e) {
e.printStackTrace();
}
return ostream.toString();
}
/**
* convert the model parser to a string and write to the file
*
* @param outfile
*/
public String toXmlDoc(String outfile) {
if (outfile == null)
return toXmlDoc();
else {
try {
OutputStream ostream = new FileOutputStream(new File(outfile));
JAXBElement<StudyCaseXmlType> element = OdmObjFactory.createPssStudyCase(getStudyCase());
createMarshaller().marshal( element, ostream );
} catch (Exception e) {
return e.toString() + " " + outfile;
}
return "ODM xml doc write to " + outfile;
}
}
}