/* Copyright (c) 2003. All Rights Reserved. * * This is the distribution of classes developed at IGPP/UCLA. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * See the COPYING file located in the top-level-directory of * the archive of this library for complete text of license. * */ package gov.nasa.pds.ppi.label; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintStream; import java.io.StringReader; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Iterator; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import org.w3c.dom.Comment; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.Text; import org.xml.sax.SAXException; /** * PDSLabel is a class that contians all information regarding a * PDS label entity. A PDS label entity consists of one or more * elements as specified in the PDS Object Defnition Language (ODL). * Each element may be a simple line of text, a block of * commented text, or a keyword/value pair. Comments and values * may extend over more than one physical line. * * @author Todd King * @author Planetary Data System * @version 1.0, 02/21/03 * @since 1.0 */ // Container for a label public class PDSLabel { /** The list of elements in the label */ public ArrayList mElement = new ArrayList(); /** The path and file name used when loading a label from a file.*/ public String mPathName = ""; /** Current line being parsed */ int mLineCount = 0; /** Stream to write messages - Default System.out */ PrintStream mLog = System.out; /** Creates an instance of a PDSLabel */ public PDSLabel() { } /** Creates an instance of a PDSLabel */ public PDSLabel(PrintStream log) { setLog(log); } /** * Returns a string with the release information for this compilation. * * @return a string contining the release information for this compilation. * @since 1.0 */ public String version() { return "1.0.0.3"; } /** * Reset all internal variables to the initial state. * * @since 1.0 */ public void reset() { mElement = new ArrayList(); } /** * Entry point for testing **/ public static void main(String args[]) { int output = 0; // Default is Dump if(args.length == 0) { System.out.println("Proper usage: pds.label.PDSLabel pathname [dump|xml]"); return; } ArrayList files = null; PDSLabel label = new PDSLabel(); try { if(label.isLabel(args[0])) { System.out.println("Parsing label: " + args[0]); label.parse(args[0]); } else { System.out.println("Parsing XML: " + args[0]); label.parseXML(args[0]); } } catch(PDSException e) { label.printMessage(e.getMessage()); e.printStackTrace(System.out); return; } if(args.length > 1) { if(args[1].compareToIgnoreCase("dump") == 0) output = 0; // Dump if(args[1].compareToIgnoreCase("xml") == 0) output = 1; // XML } switch(output) { case 1: // XML label.printXML(System.out); break; case 0: // Dump as label default: files = label.filePointers(); if(files == null) { System.out.println("No file pointers."); } else { Iterator i = files.iterator(); while(i.hasNext()) { System.out.println((String) i.next()); } } System.out.println("----------"); label.print(); break; } } /** * Determines if a file contains a PDS label. * If the first 14 characters are either "PDS_VERSION_ID" * or "CCSD3ZF0000100" then the file is considered * to contain a PDS label. * * @param pathName the fully qualified path and name of the file to parse. * * @return true if the file contains a label. * false otherwise. * * @since 1.0 */ public boolean isLabel(String pathName) { FileInputStream file; String buffer = ""; int i; int c = 0; boolean label = false; try { file = new FileInputStream(pathName); i = 0; while((c = file.read()) != -1) { // Read a little of the file buffer += (char) c; i++; if(i == 14) break; } if(buffer.compareTo("CCSD3ZF0000100") == 0) label = true; if(buffer.compareTo("PDS_VERSION_ID") == 0) label = true; } catch(IOException e) { System.out.println("Unable to open file: " + pathName); System.out.println(" Reason: " + e.getMessage()); } return label; } /** * Determines if an item is a valid. * * @param item the item to check. * * @return true if the item is valid. * false otherwise. * * @since 1.0 */ public boolean isValidItem(PDSItem item) { if(item == null) return false; return item.valid(); } /** * Parses a file containing a PDS label into its constitute elments. * The path and name of the file are passed to the method which is * opened and parsed. * * @param pathName the fully qualified path and name of the file to parse. * * @return true if the file could be opened; * false otherwise. * @since 1.0 */ public boolean parse(String pathName) throws PDSException { FileInputStream file; BufferedReader reader; boolean status; mPathName = pathName; try { file = new FileInputStream(mPathName); reader = new BufferedReader(new InputStreamReader(file)); status = parse(reader,pathName); reader.close(); file.close(); } catch(IOException e) { throw(new PDSException(e) ); } return status; } /** * Parses a Path containing a PDS label into its constitute elements. This * version use Java 1.7 Path objects. * */ public boolean parse(Path path) throws PDSException { BufferedReader reader; boolean status; mPathName = path.toString(); try { reader = Files.newBufferedReader(path, StandardCharsets.US_ASCII); status = parse(reader,mPathName); reader.close(); } catch(IOException e) { throw new PDSException(e) ; } return status; } /** * Parses a file containing a PDS label into its constitute elements. * The file to parse must be previously opened and a InputStream * pointing to the file is passed. * * @param stream a connection to a pre-opened file. * * @return true if the file could be read; * false otherwise. * @since 1.0 */ public boolean parse(InputStream stream) throws PDSException { BufferedReader reader; boolean status; try { reader = new BufferedReader(new InputStreamReader(stream)); status = parse(reader,""); reader.close(); } catch(IOException e) { throw new PDSException(e) ; } return status; } /** * Parses a file containing a PDS label into its constitute elements. * The file to parse must be previously opened and a BufferedReader * pointing to the file is passed. * * @param reader a connection to a pre-opened file. * * @return true if the file could be read; * false otherwise. * @since 1.0 */ public boolean parse(BufferedReader reader,String source) throws PDSException { boolean more = true; boolean good = true; PDSElement element; reset(); good = true; while(more) { element = new PDSElement(mLineCount,source); more = element.parse(reader); if(more) { mElement.add(element); mLineCount = element.mLineCount; } } return good; } /** * Parses a file containing XML into its constitute elments. * The path and name of the file are passed to the method which is * opened and parsed. * * @param pathName the fully qualified path and name of the file to parse. * * @return true if the file could be opened; * false otherwise. * @since 1.0 */ public boolean parseXML(String pathName) throws PDSException { FileInputStream file; boolean status; mPathName = pathName; try { file = new FileInputStream(mPathName); status = parseXML(file); file.close(); } catch(IOException e) { throw new PDSException(e) ; } return status; } /** * Parses a file containing XML into its constitute elments. * The file to parse must be previously opened and a InputStream * pointing to the file is passed. * * @param stream a connection to a pre-opened file. * * @return true if the file could be read; * false otherwise. * @since 1.0 */ public boolean parseXML(InputStream stream) throws PDSException { Document doc; DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); //factory.setValidating(true); //factory.setNamespaceAware(true); try { DocumentBuilder builder = factory.newDocumentBuilder(); doc = builder.parse(stream); } catch (ParserConfigurationException | SAXException | IOException e) { // Error generated during parsing throw new PDSException(e); } // Now transform the DOM into a "label" representation pushNode(doc, null); return true; } /** * Pushes a DOM node as a label Element. If the node has children all * children are also pushed. To push an entire DOM pass the root node. * The following rules are applied in the conversion: * * * @return true if the push was successful, * false otherwise. * @since 1.0 */ public boolean pushNode(Node node, PDSElement parentElement) throws PDSException { PDSElement element = parentElement; String buffer; NodeList list; /* switch(node.getNodeType()) { case Node.ATTRIBUTE_NODE: // The node is an Attr. case Node.CDATA_SECTION_NODE : // The node is a CDATASection. case Node.DOCUMENT_FRAGMENT_NODE: // The node is a DocumentFragment. case Node.DOCUMENT_NODE: // The node is a Document. case Node.DOCUMENT_TYPE_NODE: // The node is a DocumentType. case Node.ENTITY_NODE: // The node is an Entity. case Node.ENTITY_REFERENCE_NODE: // The node is an EntityReference. case Node.NOTATION_NODE: // The node is a Notation. case Node.PROCESSING_INSTRUCTION_NODE: // The node is a ProcessingInstruction. System.out.println(node.getNodeName()); break; case Node.ELEMENT_NODE: // The node is an Element. case Node.COMMENT_NODE: // The node is a Comment. case Node.TEXT_NODE: // The node is Text System.out.println(node.getNodeName()); break; } */ mLineCount++; // Determine if to create a new element switch(node.getNodeType()) { case Node.COMMENT_NODE: // The node is a Comment. element = new PDSElement(mLineCount); element.mType = PDSElement.TYPE_COMMENT; element.parseValue(node.getNodeValue()); mElement.add(element); break; case Node.ELEMENT_NODE: // The node is an Element. if(parentElement != null) { // Make an object if(!parentElement.isObject()) { parentElement.setValue(parentElement.mKeyword); parentElement.mKeyword = "OBJECT"; } } element = new PDSElement(mLineCount); element.mKeyword = node.getNodeName(); mElement.add(element); break; case Node.TEXT_NODE: buffer = node.getNodeValue(); buffer = buffer.trim(); if(buffer.length() > 0 && parentElement != null) { if(!parentElement.isObject()) parentElement.setValue(buffer); } break; } // Now handle all children list = node.getChildNodes(); for(int i = 0; i < list.getLength(); i++) { pushNode(list.item(i), element); } if(node.getNodeType() == Node.ELEMENT_NODE && element != null) { if(element.isObject()) { // Add END_OBJECT element = new PDSElement(mLineCount); element.mKeyword = "END_OBJECT"; element.setValue(node.getNodeName()); mElement.add(element); } } return true; } /** * Returns the fully qualified path and name of the file * which was parsed. * * @return the path and file name to the file most recently parsed. The returned value * will be blank if no file has been parsed or if a FileInputStream * was used when parsing the file. * @since 1.0 */ public String pathName() { return mPathName; } /** * Returns the path portion of the fully qualified name of the file * which was parsed. * * @return the path to the file most recently parsed. The returned value * will be blank if no file has been parsed or if a FileInputStream * was used when parsing the file. * @since 1.0 */ public String path() { int n; // Try with unix style n = mPathName.lastIndexOf('/'); if(n != -1) return mPathName.substring(0, n+1); // Try with DOS style n = mPathName.lastIndexOf('\\'); if(n != -1) return mPathName.substring(0, n+1); return ""; } /** * Find the object with the given name. * Looks for elements with the keyword "OBJECT" and a value of the * given name. If such an element is found then the corresponding * element with the keyword "END_OBJECT" is located. The passed name * can contain regular expressions. Any occurrence of a "*" in the * string is converted to the regular expression ".*" to match * any number of characters. * The search begins at the first element in the label and extends * to the last. * * @param name the name of the object to find. This can contain regular expressions. * * @return a {@link PDSItem} the indicates the start and end of the object within * the label. * * @since 1.0 */ public PDSItem findObject(String name) { return findObject(name, 0, -1); } /** * Find the object with the given name within a partion of a label. * Looks for elements with the keyword "OBJECT" and a value of the * given name. If such an element is found then the corresponding * element with the keyword "END_OBJECT" is located. The passed name * can contain regular expressions. Any occurrence of a "*" in the * string is converted to the regular expression ".*" to match * any number of characters. * The search begins at the first element indicated the {@link PDSItem} * and extends to the last. * * @param name the name of the element to find. This can contain regular expressions. * @param item the item the search is to constrained within. If item is null then the * search will span the entire label. * * @return a {@link PDSItem} the indicates the start and end of the object within * the label. * * @since 1.0 */ public PDSItem findObject(String name, PDSItem item) { if(item == null) return findObject(name); return findObject(name, item.mStart, item.mEnd); } /** * Find the next object with the given name ocurring after the passed item. * Looks for elements with the keyword "OBJECT" and a value of the * given name. If such an element is found then the corresponding * element with the keyword "END_OBJECT" is located. The passed name * can contain regular expressions. Any occurrence of a "*" in the * string is converted to the regular expression ".*" to match * any number of characters. * The search begins at the element at the endAt position in the * list and extends to the end of the label. * * @param name the name of the element to find. This can contain regular expressions. * @param item the item the search is to constrained within. If item is null then the * search will span the entire label. * * @return a {@link PDSItem} the indicates the start and end of the object within * the label. * * @since 1.0 */ public PDSItem findNextObject(String name, PDSItem item) { return findNextObject(name, item, null); } /** * Find the next object with the given name ocurring after the passed item * and within the passed object. * Looks for elements with the keyword "OBJECT" and a value of the * given name. If such an element is found then the corresponding * element with the keyword "END_OBJECT" is located. The passed name * can contain regular expressions. Any occurrence of a "*" in the * string is converted to the regular expression ".*" to match * any number of characters. * The search begins at the element at the endAt position in the * list and extends to the end of the label. * * @param name the name of the element to find. This can contain regular expressions. * @param item the item the search is to constrained within. If item is null then the * search will span the object. * @param object the object the search is to constrained within. If object is null then the * search will span the entire label. * * @return a {@link PDSItem} the indicates the start and end of the object within * the label. * * @since 1.0 */ public PDSItem findNextObject(String name, PDSItem item, PDSItem object) { if(item == null) return findObject(name, object); if(item.mEnd == -1) return null; // Can't start after end of label if(object == null) return findObject(name, item.mEnd, -1); return findObject(name, item.mEnd, object.mEnd); } /** * Find the object with the given name within a partion of a label. * Looks for elements with the keyword "OBJECT" and a value of the * given name. If such an element is found then the corresponding * element with the keyword "END_OBJECT" is located. The passed name * can contain regular expressions. Any occurrence of a "*" in the * string is converted to the regular expression ".*" to match * any number of characters. * The search begins at the element at the startAt position in the * list and extends to the element at the endAt position. * * @param name the name of the element to find. This can contain regular expressions. * @param startAt the index of the element at which to start the search. * @param endAt the index of the element at which to end the search. * * @return a {@link PDSItem} the indicates the start and end of the object within * the label. * * @since 1.0 */ public PDSItem findObject(String name, int startAt, int endAt) { int i, k; PDSElement element; PDSValue value; PDSItem item = new PDSItem(); String buffer; if(startAt == -1) startAt = 0; if(endAt == -1) endAt = mElement.size(); if(startAt >= mElement.size()) return null; if(endAt > mElement.size()) endAt = mElement.size(); name = name.replaceAll("\\*", ".*"); // Search for start of object for(i = startAt; i < endAt; i++) { element = (PDSElement) mElement.get(i); if(element.mKeyword.compareTo("OBJECT") == 0) { if(element.mValue.isEmpty()) continue; value = (PDSValue) element.mValue.get(0); buffer = value.mValue.trim(); if(buffer.matches(name)) { item.mStart = i; break; } } } if(item.mStart == -1) return null; // Not found // Search for matching end of object k = 0; for(i = item.mStart; i < endAt; i++) { element = (PDSElement) mElement.get(i); if(element.mKeyword.compareTo("OBJECT") == 0) k++; if(element.mKeyword.compareTo("END_OBJECT") == 0) { k--; if(k <= 0) { item.mEnd = i+1; break; } } } return item; // found object } /** * Find the value assocated with an element with the given name. * The passed name can contain regular expressions. Any occurrence * of a "*" in the string is converted to the regular expression ".*" * to match any number of characters. Also any occurrence of "^" is converted * to a literal "^" since it is a valid character in keywords. * The search begins at the first element in the label and extends to the last. * * @param name the name of the element to find. This can contain regular expressions. * * @return a {@link String} containing the value associated with the * named element. If the element was not found the value returned is empty. * * @since 1.0 */ public String getElementValue(String name) { return getElementValue(name, false); } /** * Find the value associated with an element with the given name. * The passed name can contain regular expressions. Any occurrence * of a "*" in the string is converted to the regular expression ".*" * to match any number of characters. Also any occurrence of "^" is converted * to a literal "^" since it is a valid character in keywords. * The search begins at the first element in the label and extends to the last. * * @param name the name of the element to find. This can contain regular expressions. * @param plain flag indicating if the value is not to be adorned * with appropriate quotation marks. * * @return a {@link String} containing the value associated with the * named element. If the element was not found the value returned is empty. * * @since 1.0 */ public String getElementValue(String name, boolean plain) { return getElementValue(name, null, plain); } /** * Find the value associated with an element with the given name within * a section of the label. * The passed name can contain regular expressions. Any occurrence * of a "*" in the string is converted to the regular expression ".*" * to match any number of characters. Also any occurrence of "^" is converted * to a literal "^" since it is a valid character in keywords. * The search begins at the first element in the label and extends to the last. * * @param name the name of the element to find. This can contain regular expressions. * @param section the section to search for the element. * @param plain flag indicating if the value is not to be adorned * with appropriate quotation marks. * * @return a {@link String} containing the value associated with the * named element. If the element was not found the value returned is empty. * * @since 1.0 */ public String getElementValue(String name, PDSItem section, boolean plain) { PDSItem item; PDSElement element; item = findItem(name, section); if(item == null) return ""; if(!item.valid()) return ""; element = getElement(item); return element.valueString(plain); } /** * Find the value associated with an element with the given name within * implied object of the a label. * The passed name can contain regular expressions. Any occurrence * of a "*" in the string is converted to the regular expression ".*" * to match any number of characters. Also any occurrence of "^" is converted * to a literal "^" since it is a valid character in keywords. * The search begins at the first element in the label and extends to the last. * * @param name the name of the element to find. This can contain regular expressions. * * @return a {@link String} containing the value associated with the * named element. If the element was not found the value returned is empty. * * @since 1.0 */ public String getElementValueInObject(String name) { return getElementValueInObject(name, null, true); } /** * Find the value associated with an element with the given name within * a section of the label without descending into sub-objects. * The passed name can contain regular expressions. Any occurrence * of a "*" in the string is converted to the regular expression ".*" * to match any number of characters. Also any occurrence of "^" is converted * to a literal "^" since it is a valid character in keywords. * The search begins at the first element in the label and extends to the last. * * @param name the name of the element to find. This can contain regular expressions. * @param section the section to search for the element. * @param plain flag indicating if the value is not to be adorned * with appropriate quotation marks. * * @return a {@link String} containing the value associated with the * named element. If the element was not found the value returned is empty. * * @since 1.0 */ public String getElementValueInObject(String name, PDSItem section, boolean plain) { PDSItem item; PDSItem object = new PDSItem(); PDSElement element; if(section != null) { object.mStart = section.mStart; object.mEnd = section.mEnd; } item = findItemInObject(name, object); if(item == null) return ""; if(!item.valid()) return ""; element = getElement(item); return element.valueString(plain); } /** * Find the item with the given name. * The passed name can contain regular expressions. Any occurrence * of a "*" in the string is converted to the regular expression ".*" * to match any number of characters. Also any occurrence of "^" is converted * to a literal "^" since it is a valid character in keywords. * The search begins at the first element in the label and extends to the last. * * @param name the name of the item to find. This can contain regular expressions. * * @return a {@link PDSItem} the indicates the start and end of the object within * the label. If there is no other objects the PDSItem is invalid. * * @since 1.0 */ public PDSItem findItem(String name) { return findItem(name, 0, -1); } /** * Find the item with the given name following the passed item in the * in the object containing the passed item. * The passed name can contain regular expressions. Any occurrence * of a "*" in the string is converted to the regular expression ".*" * to match any number of characters. Also any occurrence of "^" is converted * to a literal "^" since it is a valid character in keywords. * The search begins at the first element in the label and extends to the last. * * @param name the name of the item to find. This can contain regular expressions. * @param item a {@link PDSItem} that the search is to begin after. If item is null * then search the entire section. * @param section a {@link PDSItem} to limit the search within. If section is null * then search the entire label. * * @return a {@link PDSItem} the indicates the start and end of the object within * the label. If there is no other objects the PDSItem is invalid. * * @since 1.0 */ public PDSItem findNextItemInObject(String name, PDSItem item, PDSItem section) { int startAt = 0; int endAt = -1; if(section != null) { startAt = section.mStart+1; endAt = section.mEnd; } if(item != null) { startAt = item.mEnd; } return findItem(name, startAt, endAt, false); } /** * Find the item with the given name in the object containing the passed item. * The passed name can contain regular expressions. Any occurrence * of a "*" in the string is converted to the regular expression ".*" * to match any number of characters. Also any occurrence of "^" is converted * to a literal "^" since it is a valid character in keywords. * The search begins at the first element in the label and extends to the last. * * @param name the name of the item to find. This can contain regular expressions. * @param context a {@link PDSItem} to limit the search within. If item is null * then search the entire label. * * @return a {@link PDSItem} the indicates the start and end of the object within * the label. If there is no other objects the PDSItem is invalid. * * @since 1.0 */ public PDSItem findItemInObject(String name, PDSItem context) { return findItem(name, context.mStart, context.mEnd, false); } /** * Find the item with the given name constrained to some portion of the label. * The passed name can contain regular expressions. Any occurrence * of a "*" in the string is converted to the regular expression ".*" * to match any number of characters. Also any occurrence of "^" is converted * to a literal "^" since it is a valid character in keywords. * The search begins at the first element indicated in the passed {@link PDSItem} * and extends to the last item. * * @param name the name of the item to find. This can contain regular expressions. * @param item a {@link PDSItem} to limit the search within. If item is null * then search the entire label. * * @return a {@link PDSItem} that indicates the location of the item * within the label. If there is no other objects the PDSItem is invalid. * * @since 1.0 */ public PDSItem findItem(String name, PDSItem item) { if(item == null) return findItem(name); return findItem(name, item.mStart, item.mEnd, true); } /** * Find the item with the given name constrained to some portion of the label. * The passed name can contain regular expressions. Any occurrence * of a "*" in the string is converted to the regular expression ".*" * to match any number of characters. Also any occurrence of "^" is converted * to a literal "^" since it is a valid character in keywords. * The search begins at the first element indicated in the passed {@link PDSItem} * and extends to the last item. * * @param name the name of the item to find. This can contain regular expressions. * @param startAt the index of the item to begin the search. If startAt * is -1 then search from the beginning of the label. * @param endAt the index of the item to end the search. If endAt * is -1 then search to the end of the label. * * @return a {@link PDSItem} that indicates the location of the item * within the label. If there is no other objects the PDSItem is invalid. * * @since 1.0 */ public PDSItem findItem(String name, int startAt, int endAt) { return findItem(name, startAt, endAt, true); } /** * Find the item with the given name constrained to some portion of the label. * The passed name can contain regular expressions. Any occurrence * of a "*" in the string is converted to the regular expression ".*" * to match any number of characters. Also any occurrence of "^" is converted * to a literal "^" since it is a valid character in keywords. * The search begins at the first element indicated in the passed {@link PDSItem} * and extends to the last item. * * @param name the name of the item to find. This can contain regular expressions. * @param startAt the index of the item to begin the search. If startAt * is -1 then search from the beginning of the label. * @param endAt the index of the item to end the search. If endAt * is -1 then search to the end of the label. * @param global controls whether the search is through all elements or constrained * to the containing object * * @return a {@link PDSItem} that indicates the location of the item * within the label. If there is no other objects the PDSItem is invalid. * * @since 1.0 */ public PDSItem findItem(String name, int startAt, int endAt, boolean global) { int i, k; PDSElement element; PDSValue value; PDSItem item = new PDSItem(); PDSItem object; if(startAt == -1) startAt = 0; if(endAt == -1) endAt = mElement.size(); name = name.replaceAll("\\^", "\\\\^"); name = name.replaceAll("\\*", ".*"); // Search for start of object for(i = startAt; i < endAt; i++) { element = (PDSElement) mElement.get(i); if(element.mKeyword.matches(name)) { item.mStart = i; item.mEnd = i+1; break; } if(!global) { // Limit to elements in current object if(element.mKeyword.matches("OBJECT") && i != startAt) { // Skip object object = findObject(element.valueString(true), i, -1); if(isValidItem(object)) i += (object.mEnd - object.mStart); } } } return item; // Not found } /** * Find the next item with the given name starting at some point within the label. * The passed name can contain regular expressions. Any occurrence * of a "*" in the string is converted to the regular expression ".*" * to match any number of characters. Also any occurrence of "^" is converted * to a literal "^" since it is a valid character in keywords. * The search begins at the first element indicated in the passed {@link PDSItem} and * extends to the end of the label. * * @param name the name of the item to find. This can contain regular expressions. * @param item a {@link PDSItem} indicating where to begin the search. * * @return a {@link PDSItem} the indicates the start and end of the object within * the label. If there is no other objects the PDSItem is invalid. * * @since 1.0 */ public PDSItem findNextItem(String name, PDSItem item) { if(item == null) return findItem(name); return findItem(name, item.mEnd, -1); } /** * Return the next item after the given item. * * @param item a {@link PDSItem} indicating where to begin the search. * * @return a {@link PDSItem} the indicates the start and end of the object within * the label. If there is no other objects the PDSItem is invalid. * * @since 1.0 */ public PDSItem nextItem(PDSItem item) { PDSItem nextItem = new PDSItem(); if(item.mEnd < mElement.size()) { nextItem.mStart = item.mEnd; nextItem.mEnd = nextItem.mStart+1; } return nextItem; } /** * Return the element data associated with an item. * * @param item a {@link PDSItem} to return the element data for. The data associated with the * first element of the item is returned. * * @return a {@link PDSElement} associated with the first element of the item. * the label. If no element is associated with the item null * is returned. * * @since 1.0 */ public PDSElement getElement(PDSItem item) { if(item.valid() && item.mStart < mElement.size()) return (PDSElement) mElement.get(item.mStart); return null; } /** * Find the element with the given name. * The passed name can contain regular expressions. Any occurrence * of a "*" in the string is converted to the regular expression ".*" * to match any number of characters. Also any occurrence of "^" is converted * to a literal "^" since it is a valid character in keywords. * The search begins at the first element in the label and extends to the last. * * @param name the name of the element to find. This can contain regular expressions. * * @return a {@link PDSElement} associated with the first element of the item. * the label. If no element is associated with the item null * is returned. * * @since 1.0 */ public PDSElement getElement(String name) { PDSItem item; PDSElement element; item = findItem(name, 0, -1); if(!item.valid()) return null; element = getElement(item); return element; } /** * Find the element with the given name within a section (usually an object). * The passed name can contain regular expressions. Any occurrence * of a "*" in the string is converted to the regular expression ".*" * to match any number of characters. Also any occurrence of "^" is converted * to a literal "^" since it is a valid character in keywords. * The search begins at the first element in the label and extends to the last. * * @param name the name of the element to find. This can contain regular expressions. * @param section the item defining the section to search. * * @return a {@link PDSElement} associated with the first element of the item. * the label. If no element is associated with the item null * is returned. * * @since 1.0 */ public PDSElement getElement(String name, PDSItem section) { PDSItem item; PDSElement element; item = findItem(name, section); if(!item.valid()) return null; element = getElement(item); return element; } /** * Replace an item in a label with another label. * * @param item a {@link PDSItem} indicating the elements to replace. * @param label the label to place where item is currently. * * @since 1.0 */ public void replace(PDSItem item, PDSLabel label) { insertAfter(item, label); remove(item); } /** * Add an element to the end of a label. * * @param element the element to add to the end of the label. * * @since 1.0 */ public void add(PDSElement element) { mElement.add(element); } /** * Add a label to the end of a label. * * @param label the label to add to the end of the label. * * @since 1.0 */ public void add(PDSLabel label) { mElement.addAll(label.mElement); } /** * Insert a label before another element in this label. * If the element location is invalid nothing is done. * * @param item the location of the element before which the label is inserted. * @param label the label to place before the passed item. * * @since 1.0 */ public void insertBefore(PDSItem item, PDSLabel label) { if(!item.valid()) return; mElement.addAll(item.mStart, label.mElement); } /** * Insert a label after another element in this label. * If the location of the element is passed the end of * this label the label is appended to this label. * If the element location is invalid nothing is done. * * @param item the location of the element after which the label is inserted. * @param label the label to place after the passed item. * * @since 1.0 */ public void insertAfter(PDSItem item, PDSLabel label) { if(!item.valid()) return; if(item.mEnd >= mElement.size()) add(label); else mElement.addAll(item.mEnd, label.mElement); } /** * Insert an element before another element in this label. * If the element location is invalid nothing is done. * * @param item the location of the element before which the element is inserted. * @param element the element to place before the passed item. * * @since 1.0 */ public void insertBefore(PDSItem item, PDSElement element) { if(!item.valid()) return; mElement.add(item.mStart, element); } /** * Insert an element after another element in this label. * If the location of the element is passed the end of * this label the label is appended to this label. * If the element location is invalid nothing is done. * * @param item the location of the element after which the element is inserted. * @param element the element to place after the passed item. * * @since 1.0 */ public void insertAfter(PDSItem item, PDSElement element) { if(!item.valid()) return; if(item.mEnd >= mElement.size()) add(element); else mElement.add(item.mEnd, element); } /** * Remove a range of elements from the label. * If the range of elements in the passed item is invalid nothing is done. * * @param item the location of the elements to remove. * * @since 1.0 */ public void remove(PDSItem item) { if(!item.valid()) return; if(item.mEnd > mElement.size()) item.mEnd = mElement.size(); for(int i = item.mStart; i < item.mEnd; i++) mElement.remove(item.mStart); } /** * Extract a portion of a label into a new instance of label. * The range of elements to extract is given in a {@link PDSItem} * * @param item the location of the elements to extract. * * @since 1.0 */ public PDSLabel extract(PDSItem item) { PDSLabel label = new PDSLabel(); PDSElement element; PDSElement newElement; if(item.valid()) { for(int i = item.mStart; i < item.mEnd; i++) { element = (PDSElement) mElement.get(i); newElement = element.copy(); label.mElement.add(newElement); } } return label; } /** * Search the label and return a list to all points to files. * A pointer to a file is defined as any pointer keyword. * The first element of the value is treated as a file name. * * @return a list of file names pointed to within the label. * The list is in the form of an {@link ArrayList} of * {@link String} objects. If no pointers are found then * null is returned. * @since 1.0 */ public ArrayList filePointers() { PDSItem item = new PDSItem(); ArrayList list = new ArrayList(); PDSElement element; PDSValue value; boolean add; String temp; Iterator li ; item.empty(); item = findNextItem("^*", item); while(item.valid()) { element = getElement(item); for(int i = 0; i < element.mValue.size(); i++) { value = (PDSValue) element.mValue.get(i); if(value.mType == PDSValue.TYPE_STRING) { String buffer = new String(); buffer = value.mValue; // Check if already in list add = true; li = list.iterator(); while(li.hasNext()) { temp = (String) li.next(); if(temp.compareTo(buffer) == 0) { add = false; break; } } if(add)list.add(buffer); } } /* Not so old way if(element.mValue.size() != 0) { value = (PDSValue) element.mValue.get(0); String buffer = new String(); buffer = value.mValue; // Check if already in list add = true; Iterator i = list.iterator(); while(i.hasNext()) { temp = (String) i.next(); if(temp.compareTo(buffer) == 0) { add = false; break; } } if(add)list.add(buffer); } */ /* Old way - now we take just the first value in the list for(int i = 0; i < element.mValue.size(); i++) { value = (PDSValue) element.mValue.get(i); if(value.mType == PDSValue.TYPE_STRING) { String buffer = new String(); buffer = value.mValue; list.add(buffer); } } */ item = findNextItem("^*", item); } if(list.isEmpty()) return null; return list; } /** * Print all elements in the label according to PDS specifications * for label files using default indent and equal sign placement. * * @since 1.0 */ public void print() { print(System.out); } /** * Print all elements in the label according to PDS specifications * for label files using default indent and equal sign placement. * * @param pathName the path and name of the file to write the output to. * * @since 1.0 */ public void print(String pathName) { FileOutputStream file; PrintStream out; mPathName = pathName; try { file = new FileOutputStream(mPathName); out = new PrintStream(file); print(out); } catch(FileNotFoundException e) { System.out.println("Unable to open file: " + mPathName); System.out.println(" Reason: " + e.getMessage()); } } /** * Print all elements in the label according to PDS specifications * for label files using default indent and equal sign placement. * * @param pathName the path and name of the file to write the output to. * @param indent the number of spaces to indent each line at each level. * @param equal the position to align equal signs following keywords. * * @since 1.0 */ public void print(String pathName, int indent, int equal) { FileOutputStream file; PrintStream out; mPathName = pathName; try { file = new FileOutputStream(mPathName); out = new PrintStream(file); print(out, indent, equal); } catch(FileNotFoundException e) { System.out.println("Unable to open file: " + mPathName); System.out.println(" Reason: " + e.getMessage()); } } /** * Print all elements in the label according to PDS specifications * for label files using default indent and equal sign placement. * * @param out the stream to print the element to. * * @since 1.0 */ public void print(PrintStream out) { print(out, 2, 29, 0, -1); out.print("END\r\n"); } /** * Print all elements in the label according to PDS specifications * for label files. Each line that is output can be indented with the equal sign * (when present) placed at a fixed position. Output printed to System.out. * Each occurrence of an OBJECT is indented on level. * * @param out the stream to print the element to. * @param indent the number of spaces to indent for each level. * @param equal the number of spaces from the end of the indent * to align the equal sign for elements which have * a keyword and value. * * @since 1.0 */ public void print(PrintStream out, int indent, int equal) { print(out, indent, equal, 0, -1); out.print("END\r\n"); } /** * Print a range of elements in the label according to PDS specifications * for label files. Each line that is output can be indented with the equal sign * (when present) placed at a fixed position. Output is printed to the print stream. * Each occurrence of an OBJECT is indented on level. * * @param out the stream to print the element to. * @param indent the number of spaces to indent for each level. * @param equal the number of spaces from the end of the indent * to align the equal sign for elements which have * a keyword and value. * @param item the item to output. * * @since 1.0 */ public void print(PrintStream out, int indent, int equal, PDSItem item) { if(item == null) print(out, indent, equal); else print(out, indent, equal, item.mStart, item.mEnd); } /** * Print a range of elements in the label according to PDS specifications * for label files. Each line that is output can be indented with the equal sign * (when present) placed at a fixed position. Output is printed to the print stream. * Each occurrence of an OBJECT is indented on level. * * @param out the stream to print the element to. * @param indent the number of spaces to indent for each level. * @param equal the number of spaces from the end of the indent * to align the equal sign for elements which have * a keyword and value. * @param startAt the first element to output. * @param endAt the last element to output. * * @since 1.0 */ public void print(PrintStream out, int indent, int equal, int startAt, int endAt) { PDSElement element; PDSValue value; int i; int level = 0; if(startAt == -1) startAt = 0; if(endAt == -1) endAt = mElement.size(); for(i = startAt; i < endAt; i++) { element = (PDSElement) mElement.get(i); if(element.mKeyword.compareTo("END_OBJECT") == 0) level--; element.print(out, indent, equal, level); if(element.mKeyword.compareTo("OBJECT") == 0) level++; } } /** * Prints out the label as a set of variable definition in the PPI Ruleset lanaguage. * Each keyword is preceeded by a "$" to make it a variable definition and given * a suffix corresponding to the sequential order of the object that contains it. * For exmaple the keyword "DESCRIPTION" in the first object will have the variable * definition of "$DESCRIPTION_1", the occurrence in the second object with have * a definition of "$DESCRIPTION_2". The "OBJECT" and "END_OBJECT" elements are not * printed. There is an implicit "FILE" around any label defnition, so keywords appearing * outside any explicit object have the suffix of "_1", the first explicit object * will have a suffix of "_2". * * @param out the stream to print the element to. * * @since 1.0 */ public void printVariable(PrintStream out) { PDSElement element; PDSElement temp; int i; int occurrence = 1; int startAt = 0; int endAt = mElement.size(); for(i = startAt; i < endAt; i++) { element = (PDSElement) mElement.get(i); if(element.mKeyword.compareTo("OBJECT") == 0) { occurrence++; } if(element.mKeyword.compareTo("END") != 0){ temp = element.copy(); if(temp.mKeyword.length() != 0) { // Not empty temp.mKeyword = "$" + temp.mKeyword.replace('^', 'p') + "_" + occurrence; temp.print(out, 0, 29, 0); } } } } /** * Display the passed text as a message to System.out * Preceeds the text with a stanard phrase. * * @param text the variable portion of the message text. * * @since 1.0 */ public void printMessage(String text) { mLog.println("Unable to parse file: " + mPathName); mLog.println(" Reason: " + text); } /** * Set the log print stream * * @param stream the print stream. * * @since 1.0 */ private void setLog(PrintStream stream) { mLog = stream; } /** * Creates a DOM (Document Object Model) representation of the label. * Keywords in the label are elements in the DOM. * Values in the label are placed between the element tags. * Units are made attributes of the element. * * @return A {@link Document} object containing a representation of the label. * * @since 1.0 */ public Document getDocument() { Document doc = null; PDSElement element; try { //We need a Document DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance(); DocumentBuilder docBuilder = dbfac.newDocumentBuilder(); doc = docBuilder.newDocument(); // Create the XML tree // create the root "label" element and begin pushing elements pushObject(doc, null, "LABEL", 0); } catch (ParserConfigurationException e) { // e.printStackTrace(); doc = null; } return doc; } /** * Generates an XML representation of the label and streams it to the * print stream. * * @param out the stream to print the element to. * * @since 1.0 */ public void printXML(PrintStream out) { try { Document doc = getDocument(); //set up a transformer TransformerFactory transfac = TransformerFactory.newInstance(); try { // Need for Java 1.5 to work properly transfac.setAttribute("indent-number", 4); } catch(Exception ie) { } Transformer trans = transfac.newTransformer(getDefaultStyleSheet()); // Set up desired output format trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); trans.setOutputProperty(OutputKeys.INDENT, "yes"); //create string from xml tree // StringWriter sw = new StringWriter(); StreamResult result = new StreamResult(out); DOMSource source = new DOMSource(doc); trans.transform(source, result); // String xmlString = sw.toString(); //print xml // out.println(xmlString); } catch (IllegalArgumentException | TransformerException e) { // e.printStackTrace(); } } /** * Obtain a StreamSource to the default XML Style Sheet. * * @return a {@link StreamSource} which can be used to read the default * style sheet. * * @since 1.0 */ public StreamSource getDefaultStyleSheet() { StringReader reader = new StringReader( " \">" + "]>" + "" + "" + "" + "" + "" + "" + " " + "" + "" ); return new StreamSource(reader); } /** * Pushes (adds) a PDS label object to the document tree (DOM). The index * of the first element of of the object is passed. PDS Label elements are * added until an END_OBJECT is encounter with the given "name" or the end of * list in reached. The index of the last element pushed is returned. * This permits the calling method to walk through * element list for the PDS label and create the proper hiearchy. * * @param doc the {@link Document} to add the elements to. * @param parent the {@link Element} in doc under which to add the elements. * @param name the name to give the group of elements. * @param start the index on the first element in PDS label element list to add. * * @return the index of the last element added to the document. * * @since 1.0 */ public int pushObject(Document doc, Element parent, String name, int start) { Element elem; Element object; Comment comment; String value; String keyword; String prefix; Text text; PDSElement element; int i; boolean pointer; object = doc.createElement(name); if(parent == null) doc.appendChild(object); else parent.appendChild(object); for(i = start; i < mElement.size(); i++) { element = (PDSElement) mElement.get(i); value = element.valueString(true, false); if(element.mKeyword.matches("OBJECT")) { // Object is special i = pushObject(doc, object, value, i+1); } else { if(element.mKeyword.matches("END_OBJECT") && value.matches(name)) return i++; switch(element.mType) { case PDSElement.TYPE_COMMENT: comment = doc.createComment(element.mComment); object.appendChild(comment); break; case PDSElement.TYPE_BLANK_LINE: // Do nothing break; default: keyword = element.mKeyword; // Check for SFDU "keyword" prefix = keyword.substring(0, 4); if(prefix.compareToIgnoreCase("CCSD") == 0) { value = keyword; keyword = "SFDU"; } // Now add element // Check if pointer - adjust accordingly if(keyword.charAt(0) == '^') { // If pointer handle a little differently pointer = true; keyword = keyword.substring(1); elem = doc.createElement("POINTER"); elem.setAttribute("object", keyword); } else { // Ordinary element elem = doc.createElement(keyword); if(element.mType == PDSElement.TYPE_ORDERED) elem.setAttribute("list", "ordered"); if(element.mType == PDSElement.TYPE_UNORDERED) elem.setAttribute("list", "unordered"); } object.appendChild(elem); // Add value to child element text = doc.createTextNode(value); elem.appendChild(text); break; } } } return i; } /** Returns the label file pathname as well as the current line count */ @Override public String toString(){ return mPathName+":"+mLineCount; } }