1717import javax .xml .parsers .DocumentBuilder ;
1818import javax .xml .parsers .DocumentBuilderFactory ;
1919import javax .xml .parsers .ParserConfigurationException ;
20+ import org .apache .logging .log4j .LogManager ;
21+ import org .apache .logging .log4j .Logger ;
2022import org .w3c .dom .Document ;
2123import org .w3c .dom .Element ;
2224import org .w3c .dom .Node ;
3133 * Utility for reading DEXPI XML files and converting them into NeqSim process models.
3234 */
3335public final class DexpiXmlReader {
36+ private static final Logger logger = LogManager .getLogger (DexpiXmlReader .class );
37+
3438 private static final Map <String , EquipmentEnum > EQUIPMENT_CLASS_MAP ;
3539 private static final Map <String , EquipmentEnum > PIPING_COMPONENT_MAP ;
3640
@@ -48,6 +52,11 @@ public final class DexpiXmlReader {
4852 equipmentMap .put ("StirredTankReactor" , EquipmentEnum .Reactor );
4953 equipmentMap .put ("PlugFlowReactor" , EquipmentEnum .Reactor );
5054 equipmentMap .put ("PackedBedReactor" , EquipmentEnum .Reactor );
55+ equipmentMap .put ("Column" , EquipmentEnum .Column );
56+ equipmentMap .put ("Agitator" , EquipmentEnum .Mixer );
57+ equipmentMap .put ("Boiler" , EquipmentEnum .Heater );
58+ equipmentMap .put ("Filter" , EquipmentEnum .Separator );
59+ equipmentMap .put ("Cyclone" , EquipmentEnum .Separator );
5160 equipmentMap .put ("InlineAnalyzer" , EquipmentEnum .Calculator );
5261 equipmentMap .put ("GasAnalyzer" , EquipmentEnum .Calculator );
5362 equipmentMap .put ("Spectrometer" , EquipmentEnum .Calculator );
@@ -61,6 +70,14 @@ public final class DexpiXmlReader {
6170 pipingMap .put ("PressureSafetyValve" , EquipmentEnum .ThrottlingValve );
6271 pipingMap .put ("PressureReliefValve" , EquipmentEnum .ThrottlingValve );
6372 pipingMap .put ("PressureReducingValve" , EquipmentEnum .ThrottlingValve );
73+ pipingMap .put ("BallValve" , EquipmentEnum .ThrottlingValve );
74+ pipingMap .put ("GateValve" , EquipmentEnum .ThrottlingValve );
75+ pipingMap .put ("PlugValve" , EquipmentEnum .ThrottlingValve );
76+ pipingMap .put ("DiaphragmValve" , EquipmentEnum .ThrottlingValve );
77+ pipingMap .put ("NeedleValve" , EquipmentEnum .ThrottlingValve );
78+ pipingMap .put ("OrificePlate" , EquipmentEnum .Calculator );
79+ pipingMap .put ("FlowMeter" , EquipmentEnum .Calculator );
80+ pipingMap .put ("RuptureDisk" , EquipmentEnum .ThrottlingValve );
6481 PIPING_COMPONENT_MAP = Collections .unmodifiableMap (pipingMap );
6582 }
6683
@@ -72,8 +89,9 @@ private DexpiXmlReader() {}
7289 * @param file DEXPI XML file
7390 * @return a process system populated with units found in the XML
7491 * @throws IOException if the file cannot be read
92+ * @throws DexpiXmlReaderException if the file cannot be parsed
7593 */
76- public static ProcessSystem read (File file ) throws IOException {
94+ public static ProcessSystem read (File file ) throws IOException , DexpiXmlReaderException {
7795 return read (file , null );
7896 }
7997
@@ -86,9 +104,12 @@ public static ProcessSystem read(File file) throws IOException {
86104 * generated piping segments. If {@code null}, a methane/ethane default is used.
87105 * @return a process system populated with units found in the XML
88106 * @throws IOException if the file cannot be read
107+ * @throws DexpiXmlReaderException if the file cannot be parsed
89108 */
90- public static ProcessSystem read (File file , Stream templateStream ) throws IOException {
109+ public static ProcessSystem read (File file , Stream templateStream )
110+ throws IOException , DexpiXmlReaderException {
91111 Objects .requireNonNull (file , "file" );
112+ logger .info ("Reading DEXPI XML file: {}" , file .getAbsolutePath ());
92113 try (InputStream inputStream = new FileInputStream (file )) {
93114 return read (inputStream , templateStream );
94115 }
@@ -100,8 +121,10 @@ public static ProcessSystem read(File file, Stream templateStream) throws IOExce
100121 * @param inputStream stream containing DEXPI XML data
101122 * @return a process system populated with units found in the XML
102123 * @throws IOException if the stream cannot be read
124+ * @throws DexpiXmlReaderException if the stream cannot be parsed
103125 */
104- public static ProcessSystem read (InputStream inputStream ) throws IOException {
126+ public static ProcessSystem read (InputStream inputStream )
127+ throws IOException , DexpiXmlReaderException {
105128 return read (inputStream , null );
106129 }
107130
@@ -114,9 +137,10 @@ public static ProcessSystem read(InputStream inputStream) throws IOException {
114137 * generated piping segments. If {@code null}, a methane/ethane default is used.
115138 * @return a process system populated with units found in the XML
116139 * @throws IOException if the stream cannot be read
140+ * @throws DexpiXmlReaderException if the stream cannot be parsed
117141 */
118142 public static ProcessSystem read (InputStream inputStream , Stream templateStream )
119- throws IOException {
143+ throws IOException , DexpiXmlReaderException {
120144 ProcessSystem processSystem = new ProcessSystem ("DEXPI process" );
121145 load (inputStream , processSystem , templateStream );
122146 return processSystem ;
@@ -128,8 +152,10 @@ public static ProcessSystem read(InputStream inputStream, Stream templateStream)
128152 * @param file XML file to parse
129153 * @param processSystem target process system
130154 * @throws IOException if reading fails
155+ * @throws DexpiXmlReaderException if the file cannot be parsed
131156 */
132- public static void load (File file , ProcessSystem processSystem ) throws IOException {
157+ public static void load (File file , ProcessSystem processSystem )
158+ throws IOException , DexpiXmlReaderException {
133159 load (file , processSystem , null );
134160 }
135161
@@ -141,10 +167,12 @@ public static void load(File file, ProcessSystem processSystem) throws IOExcepti
141167 * @param templateStream stream providing default fluid, temperature, pressure, and flow rate for
142168 * generated piping segments. If {@code null}, a methane/ethane default is used.
143169 * @throws IOException if reading fails
170+ * @throws DexpiXmlReaderException if the file cannot be parsed
144171 */
145172 public static void load (File file , ProcessSystem processSystem , Stream templateStream )
146- throws IOException {
173+ throws IOException , DexpiXmlReaderException {
147174 Objects .requireNonNull (file , "file" );
175+ logger .info ("Loading DEXPI XML file: {}" , file .getAbsolutePath ());
148176 try (InputStream inputStream = new FileInputStream (file )) {
149177 load (inputStream , processSystem , templateStream );
150178 }
@@ -156,8 +184,10 @@ public static void load(File file, ProcessSystem processSystem, Stream templateS
156184 * @param inputStream XML input stream
157185 * @param processSystem target process system
158186 * @throws IOException if reading fails
187+ * @throws DexpiXmlReaderException if the stream cannot be parsed
159188 */
160- public static void load (InputStream inputStream , ProcessSystem processSystem ) throws IOException {
189+ public static void load (InputStream inputStream , ProcessSystem processSystem )
190+ throws IOException , DexpiXmlReaderException {
161191 load (inputStream , processSystem , null );
162192 }
163193
@@ -169,9 +199,10 @@ public static void load(InputStream inputStream, ProcessSystem processSystem) th
169199 * @param templateStream stream providing default fluid, temperature, pressure, and flow rate for
170200 * generated piping segments. If {@code null}, a methane/ethane default is used.
171201 * @throws IOException if reading fails
202+ * @throws DexpiXmlReaderException if the stream cannot be parsed
172203 */
173204 public static void load (InputStream inputStream , ProcessSystem processSystem ,
174- Stream templateStream ) throws IOException {
205+ Stream templateStream ) throws IOException , DexpiXmlReaderException {
175206 Objects .requireNonNull (inputStream , "inputStream" );
176207 Objects .requireNonNull (processSystem , "processSystem" );
177208
@@ -182,12 +213,13 @@ public static void load(InputStream inputStream, ProcessSystem processSystem,
182213
183214 Stream streamTemplate = templateOrDefault (templateStream );
184215
185- addEquipmentUnits (document , processSystem );
186- addPipingComponents (document , processSystem );
216+ addUnits (document , processSystem , "Equipment" , EQUIPMENT_CLASS_MAP , DexpiMetadata .TAG_NAME );
217+ addUnits (document , processSystem , "PipingComponent" , PIPING_COMPONENT_MAP ,
218+ "PipingComponentNumberAssignmentClass" );
187219 addPipingSegments (document , processSystem , streamTemplate );
188220 }
189221
190- private static Document parseDocument (InputStream inputStream ) throws IOException {
222+ private static Document parseDocument (InputStream inputStream ) throws DexpiXmlReaderException {
191223 try {
192224 DocumentBuilderFactory factory = DocumentBuilderFactory .newInstance ();
193225 factory .setFeature (XMLConstants .FEATURE_SECURE_PROCESSING , true );
@@ -203,56 +235,54 @@ private static Document parseDocument(InputStream inputStream) throws IOExceptio
203235 Document document = builder .parse (inputStream );
204236 document .getDocumentElement ().normalize ();
205237 return document ;
206- } catch (ParserConfigurationException | SAXException | IllegalArgumentException e ) {
207- throw new IOException ("Unable to parse DEXPI XML" , e );
238+ } catch (ParserConfigurationException | SAXException | IOException
239+ | IllegalArgumentException e ) {
240+ throw new DexpiXmlReaderException ("Unable to parse DEXPI XML" , e );
208241 }
209242 }
210243
211- private static void addEquipmentUnits (Document document , ProcessSystem processSystem ) {
212- NodeList equipmentNodes = document .getElementsByTagName ("Equipment" );
213- for (int i = 0 ; i < equipmentNodes .getLength (); i ++) {
214- Node node = equipmentNodes .item (i );
215- if (node .getNodeType () != Node .ELEMENT_NODE ) {
216- continue ;
217- }
218- Element element = (Element ) node ;
219- String componentClass = element .getAttribute ("ComponentClass" );
220- EquipmentEnum equipmentEnum = EQUIPMENT_CLASS_MAP .get (componentClass );
221- if (equipmentEnum == null ) {
244+ private static void addUnits (Document document , ProcessSystem processSystem , String tagName ,
245+ Map <String , EquipmentEnum > equipmentMap , String nameAttribute ) {
246+ NodeList parentNodes = document .getElementsByTagName (tagName );
247+ logger .info ("Found {} {} parent elements" , parentNodes .getLength (), tagName );
248+
249+ int totalUnits = 0 ;
250+ for (int i = 0 ; i < parentNodes .getLength (); i ++) {
251+ Node parentNode = parentNodes .item (i );
252+ if (parentNode .getNodeType () != Node .ELEMENT_NODE ) {
222253 continue ;
223254 }
255+ Element parentElement = (Element ) parentNode ;
224256
225- String baseName = firstNonEmpty (attributeValue (element , DexpiMetadata .TAG_NAME ),
226- element .getAttribute ("ID" ));
227- addDexpiUnit (processSystem , element , equipmentEnum , baseName ,
228- element .getAttribute ("ComponentClass" ));
229- }
230- }
257+ // Look for all child elements of the parent (Equipment or PipingComponent)
258+ NodeList childNodes = parentElement .getChildNodes ();
259+ for (int j = 0 ; j < childNodes .getLength (); j ++) {
260+ Node childNode = childNodes .item (j );
261+ if (childNode .getNodeType () != Node .ELEMENT_NODE ) {
262+ continue ;
263+ }
264+ Element element = (Element ) childNode ;
265+ String componentClass = element .getAttribute ("ComponentClass" );
266+ EquipmentEnum equipmentEnum = equipmentMap .get (componentClass );
267+ if (equipmentEnum == null ) {
268+ logger .warn ("Unsupported component class: {}" , componentClass );
269+ continue ;
270+ }
231271
232- private static void addPipingComponents (Document document , ProcessSystem processSystem ) {
233- NodeList componentNodes = document .getElementsByTagName ("PipingComponent" );
234- for (int i = 0 ; i < componentNodes .getLength (); i ++) {
235- Node node = componentNodes .item (i );
236- if (node .getNodeType () != Node .ELEMENT_NODE ) {
237- continue ;
272+ String baseName = firstNonEmpty (attributeValue (element , nameAttribute ),
273+ attributeValue (element , DexpiMetadata .TAG_NAME ), element .getAttribute ("ID" ));
274+ addDexpiUnit (processSystem , element , equipmentEnum , baseName ,
275+ element .getAttribute ("ComponentClass" ));
276+ totalUnits ++;
238277 }
239- Element element = (Element ) node ;
240- EquipmentEnum equipmentEnum = PIPING_COMPONENT_MAP .get (element .getAttribute ("ComponentClass" ));
241- if (equipmentEnum == null ) {
242- continue ;
243- }
244-
245- String baseName = firstNonEmpty (
246- attributeValue (element , "PipingComponentNumberAssignmentClass" ),
247- attributeValue (element , DexpiMetadata .TAG_NAME ), element .getAttribute ("ID" ));
248- addDexpiUnit (processSystem , element , equipmentEnum , baseName ,
249- element .getAttribute ("ComponentClass" ));
250278 }
279+ logger .info ("Added {} units from {} elements" , totalUnits , tagName );
251280 }
252281
253282 private static void addPipingSegments (Document document , ProcessSystem processSystem ,
254283 Stream templateStream ) {
255284 NodeList segmentNodes = document .getElementsByTagName ("PipingNetworkSegment" );
285+ logger .info ("Found {} PipingNetworkSegments" , segmentNodes .getLength ());
256286 for (int i = 0 ; i < segmentNodes .getLength (); i ++) {
257287 Node node = segmentNodes .item (i );
258288 if (node .getNodeType () != Node .ELEMENT_NODE ) {
0 commit comments