/* * (C) Copyright IBM Corp. 2013 * * LICENSE: Eclipse Public License v1.0 * http://www.eclipse.org/legal/epl-v10.html */ package com.ibm.gaiandb.webservices.parser.properties; import java.util.ArrayList; import com.ibm.gaiandb.Util; import com.ibm.gaiandb.diags.GDBMessages; import com.ibm.gaiandb.utils.Pair; import com.ibm.gaiandb.webservices.XmlElement; import com.ibm.gaiandb.webservices.parser.AttributeDefinition; import com.ibm.gaiandb.webservices.parser.NonParsableStringException; import com.ibm.gaiandb.webservices.parser.extractors.PPElementExtractor; import com.ibm.gaiandb.webservices.parser.extractors.PPElementType; import com.ibm.gaiandb.webservices.patternmatcher.TagPattern; import com.ibm.gaiandb.webservices.scanner.Tag; /** * <p> * The purpose of this class is to parse a configuration file, written in * a language defined by IBM, in order to generate a list of * {@link com.ibm.gaiandb.webservices.patternmatcher.TagPattern} objects. * <p> * The language is defined as a list of tags, which will be used as a pattern * for filling a GAIANDB VTI. * <p> * If the user wants a pattern finding the third tag 'td' in whatever tag in * a tag 'table' which id is greater than "123" and which class is "display", in * the body of a html document, the language line would be: <br/> * <html><body><table id+="123" class="display"><*><tr [3]> * <br/> * If the user wants to find the id of the tags div, in the body of a html file, * the line would be: <br/> * <html><body><div id=?> * * @author remi - IBM Hursley * */ public class GenericWsPropertiesParser { // ---------------------------------------------------------------------------------- // ----------------------------------------------------------------------- ATTRIBUTES // =========================================================================== Public // --------------------------------------------------------------------------- Static // Use PROPRIETARY notice if class contains a main() method, otherwise use // COPYRIGHT notice. public static final String COPYRIGHT_NOTICE = "(c) Copyright IBM Corp. 2013"; // -------------------------------------------------------------------------- Dynamic // ======================================================================== Protected // --------------------------------------------------------------------------- Static // -------------------------------------------------------------------------- Dynamic // ========================================================================== Private // --------------------------------------------------------------------------- Static // private static final Logger logger = new Logger( "GenericWS", 20 ); /** * Regex defining a general idea of a tag (just checks that there is not * '<' or '>' in the current scanned tag. */ private static final String REGEX_LINE = "^(\\s*<[^<>]*>)*\\s*$"; /** Regex defining the end of a tag. */ private static final String REGEX_SPLIT_TAGS = "(>\\s*)"; /** Regex defining the beginning of a tag. */ private static final String REGEX_REMOVE_START_TAG = "\\s*<"; // /** // * Defines the value of an attribute, found in the config file, when the // * purpose of the pattern is to find an attribute value. // * <p> // * If the user wants to find the id of the tags div, in the body of a html file, // * the line would be: <br/> // * <html><body><div id=?> // */ // private static final String ATTRIBUTE_TO_FIND = "?"; // -------------------------------------------------------------------------- Dynamic /** The String which will be parsed into a list of * {@link com.ibm.gaiandb.webservices.patternmatcher.TagPattern} * objects. */ private String lineToCompile = ""; /** * The object which is looked for by a {@link com.ibm.gaiandb.webservices.patternmatcher.TagMatcher} * object. * <p> * When the {@link com.ibm.gaiandb.webservices.parser.properties.GenericWsPropertiesParser#kindOfRequest} * is of type {@link com.ibm.gaiandb.webservices.XmlElement#VALUE}, this * attribute is null, when it is of type * {@link com.ibm.gaiandb.webservices.XmlElement#TAG_ATTIBUTE}, this attribute * is a Pair<String, Integer> object, with the name of an attribute as a first * value, and the index of the tag the user wants the attribute of as the * second element. */ private Object returnObject = null; /** * Defines if the request of the line that the object has to parse is * looking for a value, tag attribute... */ private XmlElement kindOfRequest; // ---------------------------------------------------------------------------------- // ---------------------------------------------------------------------------- TOOLS // ---------------------------------------------------------------------------------- // -------------------------------------------------------------------------- METHODS // ===================================================================== Constructors // --------------------------------------------------------------------------- Public /** * Generates a GenericWsPropertiesParser object. * @param line * The line to parse for generating the object. */ public GenericWsPropertiesParser(String line) { if (line != null) { this.lineToCompile = line; } this.kindOfRequest = XmlElement.UNDEFINED; } // -------------------------------------------------------------------------- Private // =========================================================================== Public // --------------------------------------------------------------------------- Static // -------------------------------------------------------------------------- Dynamic /** * Parses the line defining the object into a list of TagPattern objects. * @return The TagPattern list specified by the String defining the current * object. */ public ArrayList<TagPattern> parseTags() throws NonParsableStringException { this.returnObject = null; ArrayList<TagPattern> ret = new ArrayList<TagPattern>(); // Checks that the line has the right format if (this.lineToCompile.matches(REGEX_LINE)) { // Splits the line with the character of end of tag '>' String[] tags = this.lineToCompile.split(REGEX_SPLIT_TAGS); int index = 0; // for each content of tag: for (String tag : tags) { TagPattern tagFound; // remove the beginning of the tag: '<' // and parses the tag String tagContent = tag.replaceFirst(REGEX_REMOVE_START_TAG, ""); tagFound = this.lineToTag(tagContent, index++); // Saves the new parsed TagPattern ret.add(tagFound); } // Once all the parsing is done, if the kindOfRequest is still UNDEFINED, it // is considered as looking for the value of a tag by default if (this.kindOfRequest == XmlElement.UNDEFINED) { this.kindOfRequest = XmlElement.VALUE; } } return ret; } /** * Returns the XmlElement defining the type of the sent request. * @return the XmlElement defining the type of the sent request. */ public XmlElement getRequestType() { return this.kindOfRequest; } /** * Returns the Object defining the qualifiers of the sent request. * <p> * Depending on the value of the method getRequestType(), the * returned type will be: * <table border="1"> * <tr> * <th>value from getRequestType()</th> * <th>type returned by getRequestQualifiers()</th> * <th>Meaning</th> * </tr> * <tr> * <td>VALUE</td> * <td>null</td> * <td></td> * </tr> * <tr> * <td>TAG_ATTIBUTE</td> * <td>Pair<String, Integer></td> * <td>String: the name of the attribute to return<br/> * Integer: the depth of the tag which attribute needs to be found</td> * </tr> * <tr> * <td>ERROR_TAG</td> * <td>null</td> * <td></td> * </tr> * <tr> * <td>UNDEFINED</td> * <td>null</td> * <td></td> * </tr> * </table> * * @return the Object defining the qualifiers of the sent request. */ public Object getRequestQualifiers() { return this.returnObject; } // ======================================================================== Protected // --------------------------------------------------------------------------- Static // -------------------------------------------------------------------------- Dynamic // ========================================================================== Private // --------------------------------------------------------------------------- Static // -------------------------------------------------------------------------- Dynamic /** * Parses the content of a tag (in a String) into a * TagPattern object. * * @param tagContent * The String to parse into a tag. * A NullPointerException will be thrown if * this parameter is null. * @param tagId * eeerh? * * @return a TagPattern defined in the tagContent. * * @throws NonParsableStringException * If the String cannot be parsed into a tag. */ private TagPattern lineToTag(String tagContent, int tagId) throws NonParsableStringException { // The name of the tag currently read String name = null; // List of attribute definitions // - element.getFirst() is the attribute's name // - element.getSecond().getFirst() is the operation defining // the attribute declaration // - element.getSecond().getSecond() is the attribute's value ArrayList<AttributeDefinition> attributes = new ArrayList<AttributeDefinition>(); // index position of the tag in its parent's list of children int position = Tag.NO_INDEX_POSITION; // The TagPattern to return TagPattern retTag = null; // Gets the different declarations in the tagContent String String[] tagObjects = Util.splitByTrimmedDelimiterNonNestedInCurvedBracketsOrDoubleQuotes(tagContent, ' '); // For each declaration for (String contentPart : tagObjects) { // --- Defines which Properties Parser element type it is // (name of the tag, attribute declaration, position...) PPElementType type = PPElementType.NONE; PPElementExtractor extractor = null; for (PPElementExtractor extr : PPElementExtractor.extractors) { if (extr.canExtract(contentPart)) { type = extr.getExtractedType(); extractor = extr; break; } } // --- Extracts the data considering the received type of // Properties Parser element switch (type) { case NAME: if (name != null) { this.kindOfRequest = XmlElement.ERROR_TAG; throw new NonParsableStringException( GDBMessages.DSWRAPPER_GENERICWS_PROPERTY_PARSING_DOUBLE_NAME_DEFINITION, "The tag which content is \"" + tagContent + "\" has at least two name definitions."); } //else: name not defined twice name = (String)extractor.extract(contentPart); break; case ATTRIBUTE: // Extract the attribute and its properties // - element.getFirst() is the attribute's name // - element.getSecond().getFirst() is the operation defining // the attribute declaration // - element.getSecond().getSecond() is the attribute's value AttributeDefinition att_val = (AttributeDefinition)extractor.extract(contentPart); attributes.add(att_val); break; case ATTRIBUTE_TO_FIND: // Checks if the kindOfRequest has already been set up at TAG_ATTRIBUTE if (this.kindOfRequest != XmlElement.TAG_ATTIBUTE) { this.kindOfRequest = XmlElement.TAG_ATTIBUTE; // Gets the name of the attribute to be found String AttributeName = (String)extractor.extract(contentPart); this.returnObject = new Pair<String, Integer>(AttributeName, new Integer(tagId)); } // If the kindOfRequest has already been set up at TAG_ATTRIBUTE, // that means there is already an attribute to be found else { // an att=? already exists this.kindOfRequest = XmlElement.ERROR_TAG; throw new NonParsableStringException( GDBMessages.DSWRAPPER_GENERICWS_PROPERTY_PARSING_DOUBLE_ATTRIBUTE_TO_LOOK_FOR_DEFINITION, "There are at least two attributes to " + "find ( attr1=? attr2=? ) in this GenericWS property."); } break; case POSITION: if (position != Tag.NO_INDEX_POSITION) { this.kindOfRequest = XmlElement.ERROR_TAG; throw new NonParsableStringException( GDBMessages.DSWRAPPER_GENERICWS_PROPERTY_PARSING_DOUBLE_POSITION_DEFINITION, "The tag which content is \"" + tagContent + "\" has at least two position definitions ( [pos1] [pos2] )."); } Integer pos = (Integer)extractor.extract(contentPart); if (pos != null) { position = pos.intValue() - 1; // -1 because the internal // representation for the position is // 0-based and the one for the user // interface is 1-based } // else stays null case NONE: default: // The application shouldn't be able to arrive here break; } } if (name == null) { this.kindOfRequest = XmlElement.ERROR_TAG; throw new NonParsableStringException( GDBMessages.DSWRAPPER_GENERICWS_PROPERTY_PARSING_NAME_NOT_DEFINED, "The tag does not have any names."); } // --- Generates the new tagPattern... retTag = new TagPattern(name, position); // ... and complete it for (AttributeDefinition attribute : attributes) { retTag.addAttribute(attribute); } return retTag; } }