Package org.eclipse.ui.internal.cheatsheets.data

Source Code of org.eclipse.ui.internal.cheatsheets.data.CheatSheetParser

/*******************************************************************************
* Copyright (c) 2002, 2007 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*     IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.ui.internal.cheatsheets.data;

import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.net.URL;
import java.util.ArrayList;

import javax.xml.parsers.DocumentBuilder;

import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.help.internal.UAElement;
import org.eclipse.help.internal.UAElementFactory;
import org.eclipse.help.internal.dynamic.DocumentProcessor;
import org.eclipse.help.internal.dynamic.DocumentReader;
import org.eclipse.help.internal.dynamic.ExtensionHandler;
import org.eclipse.help.internal.dynamic.FilterHandler;
import org.eclipse.help.internal.dynamic.IncludeHandler;
import org.eclipse.help.internal.dynamic.ProcessorHandler;
import org.eclipse.osgi.util.NLS;
import org.eclipse.ui.cheatsheets.AbstractItemExtensionElement;
import org.eclipse.ui.internal.cheatsheets.CheatSheetEvaluationContext;
import org.eclipse.ui.internal.cheatsheets.CheatSheetPlugin;
import org.eclipse.ui.internal.cheatsheets.ICheatSheetResource;
import org.eclipse.ui.internal.cheatsheets.Messages;
import org.eclipse.ui.internal.cheatsheets.composite.model.CompositeCheatSheetModel;
import org.eclipse.ui.internal.cheatsheets.composite.parser.CompositeCheatSheetParser;
import org.eclipse.ui.internal.cheatsheets.composite.parser.ICompositeCheatsheetTags;
import org.eclipse.ui.internal.cheatsheets.composite.parser.IStatusContainer;
import org.eclipse.ui.internal.cheatsheets.registry.CheatSheetItemExtensionElement;
import org.eclipse.ui.internal.cheatsheets.registry.CheatSheetRegistryReader;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

/**
* Parser for the cheatsheet content files.
*
* Construct an intance of the CheatSheetDomParser.
* Call <code>parse()</code>.
* Then get the content items by calling
* <code>getIntroItem()</code> and <code>getItemsList()</code>.
* The title of the cheatsheet can be retrieved by calling
* <code>getTitle()</code>.
*
*/
public class CheatSheetParser implements IStatusContainer {

  private static final String TRUE_STRING = "true"; //$NON-NLS-1$

  private DocumentBuilder documentBuilder;
  private DocumentProcessor processor;
  private ArrayList itemExtensionContainerList;
 
  // Cheatsheet kinds that can be parsed
  public static final int COMPOSITE_ONLY = 1;
  public static final int SIMPLE_ONLY = 2;
  public static final int ANY = 3;
 
  private IStatus status;

  private int commandCount;

  private int actionCount;
 

  /**
   * Java constructor comment.
   */
  public CheatSheetParser() {
    super();
    documentBuilder = CheatSheetPlugin.getPlugin().getDocumentBuilder();
  }
 
  /**
   *  Gets the status of the last call to parse()
   */
  public IStatus getStatus() {
    return status;
  }
 
  public void addStatus(int severity, String message, Throwable exception) {
    status = ParserStatusUtility.addStatus(status, severity, message, exception);
  }

  /**
   * Converts any characters required to escaped by an XML parser to
   * their escaped counterpart.
   *
   * Characters      XML escaped counterpart
   * <      ->    &lt;
   * >      ->    &gt;
   * &      ->    &amp;
   * '      ->    &apos;
   * "      ->    &quot;
   *
   * Tags that will be ignored <b>, </b> and <br/>.
   *
   * @param text the string buffer to have its characters escaped
   * @return string buffer with any of the characters requiring XML escaping escaped
   */
  private StringBuffer escapeXMLCharacters(StringBuffer text) {
    // Set the maximum length of the tags to ignore
    final int MAXIMUM_TAG_LENGTH = 5;
   
    // Keep a local variable for the orignal string's length
    int length = text.length();
   
    // Create the buffer to store the resulting string
    StringBuffer result = new StringBuffer(length);
   
    // Loop for the characters of the original string
    for(int i=0; i<length; i++) {
      // Grab the next character and determine how to handle it
      char c = text.charAt(i);
      switch (c) {
        case '<': {
          // We have a less than, grab the maximum tag length of characters
          // or the remaining characters which follow and determine if it
          // is the start of a tag to ignore.
          String tmp = ICheatSheetResource.EMPTY_STRING;
          if(i+MAXIMUM_TAG_LENGTH < length)
            tmp = text.substring(i, i+MAXIMUM_TAG_LENGTH).toLowerCase();
          else {
            tmp = text.substring(i, length).toLowerCase();
          }
          if(tmp.startsWith(IParserTags.BOLD_START_TAG) || tmp.startsWith(IParserTags.BOLD_END_TAG) || tmp.startsWith(IParserTags.BREAK_TAG)) {
            // We have a tag to ignore so just emit the character
            result.append(c);
          } else {
            // We have detemined that it is just a less than
            // so emit the XML escaped counterpart
            result.append(IParserTags.LESS_THAN);
          }
          break; }
        case '>': {
          // We have a greater than, grab the maximum tag length of characters
          // or the starting characters which come before and determine if it
          // is the end of a tag to ignore.
          String tmp = ICheatSheetResource.EMPTY_STRING;
          if(i>=MAXIMUM_TAG_LENGTH) {
            tmp = text.substring(i-MAXIMUM_TAG_LENGTH, i+1).toLowerCase();
          } else {
            tmp = text.substring(0, i+1).toLowerCase();
          }
          if(tmp.endsWith(IParserTags.BOLD_START_TAG) || tmp.endsWith(IParserTags.BOLD_END_TAG) || tmp.endsWith(IParserTags.BREAK_TAG)) {
            // We have a tag to ignore so just emit the character
            result.append(c);
          } else {
            // We have detemined that it is just a greater than
            // so emit the XML escaped counterpart
            result.append(IParserTags.GREATER_THAN);
          }
          break; }
        case '&':
          // We have an ampersand so emit the XML escaped counterpart
          result.append(IParserTags.AMPERSAND);
          break;
        case '\'':
          // We have an apostrophe so emit the XML escaped counterpart
          result.append(IParserTags.APOSTROPHE);
          break;
        case '"':
          // We have a quote so emit the XML escaped counterpart
          result.append(IParserTags.QUOTE);
          break;
        case '\t':
          // We have a tab, replace with a space
          result.append(' ');
          break;
        default:
          // We have a character that does not require escaping
          result.append(c);
          break;
      }
    }
    return result;
  }

  private Node findNode(Node startNode, String nodeName) {
    if(startNode == null) {
      return null;
    }

    if(startNode.getNodeName().equals(nodeName)) {
      return startNode;
    }

    NodeList nodes = startNode.getChildNodes();
    for (int i = 0; i < nodes.getLength(); i++) {
      Node node = nodes.item(i);
      if(node.getNodeName().equals(nodeName)) {
        return node;
      }
    }
   
    return null;
  }

  private void handleExecutable(IExecutableItem item, Node executableNode, AbstractExecutable executable) throws CheatSheetParserException {
    Assert.isNotNull(item);
    Assert.isNotNull(executableNode);

    String[] params = null;

    if (executable instanceof CheatSheetCommand) {
      commandCount++;
    }
    if (executable instanceof Action) {
      actionCount++;
    }

    NamedNodeMap attributes = executableNode.getAttributes();
    if (attributes != null) {
      for (int x = 0; x < attributes.getLength(); x++) {
        Node attribute = attributes.item(x);
        String attributeName = attribute.getNodeName();
        if (attribute == null || attributeName == null)
          continue;     
        if (attributeName.equals(IParserTags.CONFIRM)) {
          executable.setConfirm(attribute.getNodeValue().equals(TRUE_STRING));}
        else if (attributeName.equals(IParserTags.WHEN)) {
          executable.setWhen(attribute.getNodeValue());
        } else if (attributeName.equals(IParserTags.REQUIRED)) {
          executable.setRequired(attribute.getNodeValue().equals(TRUE_STRING));
        } else if (attributeName.equals(IParserTags.TRANSLATE)) {
          // Translation hint, no semantic effect
        } else if (executable.hasParams() && attributeName.startsWith(IParserTags.PARAM)) {
          try {
            if(params == null) {
              params = new String[9];
            }
            String paramNum = attributeName.substring(IParserTags.PARAM.length());
            int num = Integer.parseInt(paramNum)-1;
           
            if(num>-1 && num<9){
              params[num] = attribute.getNodeValue();
            } else {
              String message = NLS.bind(Messages.ERROR_PARSING_PARAM_INVALIDRANGE, (new Object[] {attributeName, paramNum}));
              addStatus(IStatus.ERROR, message, null);
            }
          } catch(NumberFormatException e) {
            String message = Messages.ERROR_PARSING_PARAM_INVALIDNUMBER;
            addStatus(IStatus.ERROR, message, e);
          }
        } else if (!executable.handleAttribute(attribute)) {
          String message = NLS.bind(Messages.WARNING_PARSING_UNKNOWN_ATTRIBUTE, (new Object[] {attributeName, executableNode.getNodeName()}));
          addStatus(IStatus.WARNING, message, null);
        }
      }
      String errorMessage = executable.checkAttributes(executableNode);
      if (errorMessage != null) {
        throw new CheatSheetParserException(errorMessage);
      }
    }
        checkForNoChildren(executableNode);
    executable.setParams(params);
    item.setExecutable(executable);
  }

  private void checkForNoChildren(Node parentNode) {
    NodeList nodes = parentNode.getChildNodes();
    for (int i = 0; i < nodes.getLength(); i++) {
      Node node = nodes.item(i);
      if(node.getNodeType() != Node.TEXT_NODE && node.getNodeType() != Node.COMMENT_NODE ) {
        String message = NLS.bind(Messages.WARNING_PARSING_UNKNOWN_ELEMENT, (new Object[] {node.getNodeName(), parentNode.getNodeName()}));
        addStatus(IStatus.WARNING, message, null);
      }
    }
  }

  private void handleCheatSheetAttributes(CheatSheet cheatSheet, Node cheatSheetNode) throws CheatSheetParserException {
    Assert.isNotNull(cheatSheet);
    Assert.isNotNull(cheatSheetNode);
    Assert.isTrue(cheatSheetNode.getNodeName().equals(IParserTags.CHEATSHEET));

    boolean title = false;

    NamedNodeMap attributes = cheatSheetNode.getAttributes();
    if (attributes != null) {
      for (int x = 0; x < attributes.getLength(); x++) {
        Node attribute = attributes.item(x);
        String attributeName = attribute.getNodeName();
        if (attribute == null || attributeName == null)
          continue;

        if (attributeName.equals(IParserTags.TITLE)) {
          title = true;
          cheatSheet.setTitle(attribute.getNodeValue());
        } else {
          String message = NLS.bind(Messages.WARNING_PARSING_UNKNOWN_ATTRIBUTE, (new Object[] {attributeName, cheatSheetNode.getNodeName()}));
          addStatus(IStatus.WARNING, message, null);
        }
      }
    }

    if(!title) {
      String message = NLS.bind(Messages.ERROR_PARSING_NO_TITLE, (new Object[] {cheatSheetNode.getNodeName()}));
      throw new CheatSheetParserException(message);
    }
  }

  private void handleConditionalSubItem(Item item, Node conditionalSubItemNode) throws CheatSheetParserException {
    Assert.isNotNull(item);
    Assert.isNotNull(conditionalSubItemNode);
    Assert.isTrue(conditionalSubItemNode.getNodeName().equals(IParserTags.CONDITIONALSUBITEM));

    ConditionalSubItem conditionalSubItem = new ConditionalSubItem();

    boolean condition = false;

    // Handle attributes
    NamedNodeMap attributes = conditionalSubItemNode.getAttributes();
    if (attributes != null) {
      for (int x = 0; x < attributes.getLength(); x++) {
        Node attribute = attributes.item(x);
        String attributeName = attribute.getNodeName();
        if (attribute == null || attributeName == null)
          continue;

        if (attributeName.equals(IParserTags.CONDITION)) {
          condition = true;
          conditionalSubItem.setCondition(attribute.getNodeValue());
        } else {
          String message = NLS.bind(Messages.WARNING_PARSING_UNKNOWN_ATTRIBUTE, (new Object[] {attributeName, conditionalSubItemNode.getNodeName()}));
          addStatus(IStatus.WARNING, message, null);
        }
      }
    }

    if(!condition) {
      String message = NLS.bind(Messages.ERROR_PARSING_NO_CONDITION, (new Object[] {conditionalSubItemNode.getNodeName()}));
      throw new CheatSheetParserException(message);
    }

    boolean subitem = false;

    // Handle nodes
    NodeList nodes = conditionalSubItemNode.getChildNodes();
    for (int i = 0; i < nodes.getLength(); i++) {
      Node node = nodes.item(i);

      if(node.getNodeName().equals(IParserTags.SUBITEM)) {
        subitem = true;
        handleSubItem(conditionalSubItem, node);
      } else {
        if(node.getNodeType() != Node.TEXT_NODE && node.getNodeType() != Node.COMMENT_NODE ) {
          String message = NLS.bind(Messages.WARNING_PARSING_UNKNOWN_ELEMENT, (new Object[] {node.getNodeName(), conditionalSubItemNode.getNodeName()}));
          addStatus(IStatus.WARNING, message, null);
        }
      }
    }

    if(!subitem) {
      String message = NLS.bind(Messages.ERROR_PARSING_NO_SUBITEM, (new Object[] {conditionalSubItemNode.getNodeName()}));
      throw new CheatSheetParserException(message);
    }

    item.addSubItem(conditionalSubItem);
  }

  private void handleDescription(Item item, Node startNode) throws CheatSheetParserException {
    Assert.isNotNull(item);
    Assert.isNotNull(startNode);

    Node descriptionNode = findNode(startNode, IParserTags.DESCRIPTION);
   
    if(descriptionNode != null) {
      String text = handleMarkedUpText(descriptionNode, startNode, IParserTags.DESCRIPTION);
      item.setDescription(text);
    } else {
      Node parentNode = startNode;
      if( startNode.getNodeName().equals(IParserTags.DESCRIPTION) ) {
        parentNode = startNode.getParentNode();
      }
      String message = NLS.bind(Messages.ERROR_PARSING_NO_DESCRIPTION, (new Object[] {parentNode.getNodeName()}));
      throw new CheatSheetParserException(message);
    }
  }

  private String handleMarkedUpText(Node nodeContainingText, Node startNode, String nodeName ) {
    NodeList nodes = nodeContainingText.getChildNodes()
    StringBuffer text = new StringBuffer();
   
    boolean containsMarkup = false;
   
    // The documentation for the content file specifies
    // that leading whitespace should be ignored at the
    // beginning of a description or after a <br/>. This
    // applies also to <onCompletion> elements.
    // See Bug 129208 and Bug 131185
    boolean isLeadingTrimRequired = true;

    for (int i = 0; i < nodes.getLength(); i++) {
      Node node = nodes.item(i);
      if(node.getNodeType() == Node.TEXT_NODE) {
        String nodeValue = node.getNodeValue();
          if (isLeadingTrimRequired) {
          nodeValue = trimLeadingWhitespace(nodeValue);
        }
        text.append(nodeValue);
        isLeadingTrimRequired = false;
      } else if(node.getNodeType() == Node.ELEMENT_NODE) {
        // handle <b></b> and <br/>
        if(node.getNodeName().equals(IParserTags.BOLD)) {
          containsMarkup = true;
          text.append(IParserTags.BOLD_START_TAG);
          text.append(node.getFirstChild().getNodeValue());
          text.append(IParserTags.BOLD_END_TAG);
          isLeadingTrimRequired = false;
        } else if(node.getNodeName().equals(IParserTags.BREAK)) {
          containsMarkup = true
          text.append(IParserTags.BREAK_TAG);
          isLeadingTrimRequired = true;
        } else {
          warnUnknownMarkupElement(startNode, nodeName, node);
        }
      }
    }

    if(containsMarkup) {
      text = escapeXMLCharacters(text);
      text.insert(0, IParserTags.FORM_START_TAG);
      text.append(IParserTags.FORM_END_TAG);
    } else {
      deTab(text);
    }

    // Remove the new line, form feed and tab chars
    return text.toString().trim();
  }

  // Replace any tabs with spaces
 
  private void deTab(StringBuffer text) {
    for (int i = 0; i < text.length(); i++) {
      if (text.charAt(i) == '\t') {
        text.setCharAt(i, ' ');
      }
    }
  }

  private String trimLeadingWhitespace(String nodeValue) {
    int firstNonWhitespaceIndex = 0;
    while (firstNonWhitespaceIndex < nodeValue.length() &&
        Character.isWhitespace(nodeValue.charAt(firstNonWhitespaceIndex))) {
      firstNonWhitespaceIndex++;
    }
    if (firstNonWhitespaceIndex > 0) {
        return nodeValue.substring(firstNonWhitespaceIndex, nodeValue.length());
    }
    return nodeValue;
  }

  /*
   * Write a warning to the log
   */
  private void warnUnknownMarkupElement(Node startNode, String nodeName, Node node) {
    Node parentNode = startNode;
    if( startNode.getNodeName().equals(nodeName) ) {
      parentNode = startNode.getParentNode();
    }
    String message;
    if (IParserTags.DESCRIPTION.equals(nodeName)) {
        message = NLS.bind(Messages.WARNING_PARSING_DESCRIPTION_UNKNOWN_ELEMENT, (new Object[] {parentNode.getNodeName(), node.getNodeName()}));
    } else {
      message = NLS.bind(Messages.WARNING_PARSING_ON_COMPLETION_UNKNOWN_ELEMENT, (new Object[] {parentNode.getNodeName(), node.getNodeName()}));
    }
    addStatus(IStatus.WARNING, message, null);

  }
 
  private void handleOnCompletion(Item item, Node onCompletionNode) {
    String text = handleMarkedUpText(onCompletionNode, onCompletionNode, IParserTags.ON_COMPLETION);
    item.setCompletionMessage(text);
  }
 
  private void handleIntroNode(CheatSheet cheatSheet, Node introNode)
      throws CheatSheetParserException {
    Item introItem = new Item();
    introItem.setTitle(Messages.CHEAT_SHEET_INTRO_TITLE);

    handleIntroAttributes(introItem, introNode);
   
        boolean hasDescription = false;
   
    NodeList nodes = introNode.getChildNodes();
    for (int i = 0; i < nodes.getLength(); i++) {
      Node node = nodes.item(i);

      if(node.getNodeName().equals(IParserTags.DESCRIPTION)) {
        if (hasDescription) {
          String message = NLS.bind(Messages.ERROR_PARSING_MULTIPLE_DESCRIPTION, (new Object[] {introNode.getNodeName()}));
          addStatus(IStatus.ERROR, message, null);
        } else {
            hasDescription = true;
            handleDescription(introItem, node);
        }
      } else {
        if(node.getNodeType() != Node.TEXT_NODE && node.getNodeType() != Node.COMMENT_NODE ) {
          String message = NLS.bind(Messages.WARNING_PARSING_UNKNOWN_ELEMENT, (new Object[] {node.getNodeName(), introNode.getNodeName()}));
          addStatus(IStatus.WARNING, message, null);
        }
      }
    }

    if(!hasDescription) {
      String message = NLS.bind(Messages.ERROR_PARSING_NO_DESCRIPTION, (new Object[] {introNode.getNodeName()}));
      addStatus(IStatus.ERROR, message, null);
    }

    cheatSheet.setIntroItem(introItem);
  }

  private void handleIntroAttributes(Item item, Node introNode) {
    Assert.isNotNull(item);
    Assert.isNotNull(introNode);

    NamedNodeMap attributes = introNode.getAttributes();
    if (attributes != null) {
      for (int x = 0; x < attributes.getLength(); x++) {
        Node attribute = attributes.item(x);
        String attributeName = attribute.getNodeName();
        if (attribute == null || attributeName == null)
          continue;

        if (attributeName.equals(IParserTags.CONTEXTID)) {
          item.setContextId(attribute.getNodeValue());
        } else if (attributeName.equals(IParserTags.HREF)) {
          item.setHref(attribute.getNodeValue());
        } else {
          String message = NLS.bind(Messages.WARNING_PARSING_UNKNOWN_ATTRIBUTE, (new Object[] {attributeName, introNode.getNodeName()}));
          addStatus(IStatus.WARNING, message, null);
        }
      }
    }
  }

  private Item handleItem(Node itemNode) throws CheatSheetParserException {
    Assert.isNotNull(itemNode);
    Assert.isTrue(itemNode.getNodeName().equals(IParserTags.ITEM));

    Item item = new Item();

    handleItemAttributes(item, itemNode);

    boolean hasDescription = false;
   
    NodeList nodes = itemNode.getChildNodes();
   
    IncompatibleSiblingChecker checker = new IncompatibleSiblingChecker(this, itemNode);
    for (int i = 0; i < nodes.getLength(); i++) {
      Node node = nodes.item(i);
      checker.checkElement(node.getNodeName());
      if(node.getNodeName().equals(IParserTags.ACTION)) {
        handleExecutable(item, node, new Action());
      } else if(node.getNodeName().equals(IParserTags.COMMAND)) {
        handleExecutable(item, node, new CheatSheetCommand());
      } else if(node.getNodeName().equals(IParserTags.DESCRIPTION)) {
        if (hasDescription) {
          String message = NLS.bind(Messages.ERROR_PARSING_MULTIPLE_DESCRIPTION, (new Object[] {itemNode.getNodeName()}));
          addStatus(IStatus.ERROR, message, null);
        } else {
            hasDescription = true;
            handleDescription(item, node);
        }
      } else if(node.getNodeName().equals(IParserTags.ON_COMPLETION)) {
        handleOnCompletion(item, node);
      } else if(node.getNodeName().equals(IParserTags.SUBITEM)) {
        handleSubItem(item, node);
      } else if(node.getNodeName().equals(IParserTags.CONDITIONALSUBITEM)) {
        handleConditionalSubItem(item, node);
      } else if(node.getNodeName().equals(IParserTags.REPEATEDSUBITM)) {
        handleRepeatedSubItem(item, node);
      } else if(node.getNodeName().equals(IParserTags.PERFORMWHEN)) {
        handlePerformWhen(item, node);
      } else {
        if(node.getNodeType() != Node.TEXT_NODE && node.getNodeType() != Node.COMMENT_NODE ) {
          String message = NLS.bind(Messages.WARNING_PARSING_UNKNOWN_ELEMENT, (new Object[] {node.getNodeName(), itemNode.getNodeName()}));
          addStatus(IStatus.WARNING, message, null);
        }
      }
    }

    if(!hasDescription) {
      String message = NLS.bind(Messages.ERROR_PARSING_NO_DESCRIPTION, (new Object[] {itemNode.getNodeName()}));
      addStatus(IStatus.ERROR, message, null);
    }
   
    return item;
  }

  private void handleItemAttributes(Item item, Node itemNode) throws CheatSheetParserException {
    Assert.isNotNull(item);
    Assert.isNotNull(itemNode);

    ArrayList itemExtensionElements = new ArrayList();

    boolean title = false;

    NamedNodeMap attributes = itemNode.getAttributes();
    if (attributes != null) {
      for (int x = 0; x < attributes.getLength(); x++) {
        Node attribute = attributes.item(x);
        String attributeName = attribute.getNodeName();
        if (attribute == null || attributeName == null)
          continue;

        if (attributeName.equals(IParserTags.TITLE)) {
          title = true;
          item.setTitle(attribute.getNodeValue());
        } else if (attributeName.equals(IParserTags.CONTEXTID)) {
          item.setContextId(attribute.getNodeValue());
        } else if (attributeName.equals(IParserTags.HREF)) {
          item.setHref(attribute.getNodeValue());
        } else if (attributeName.equals(IParserTags.SKIP)) {
          item.setSkip(attribute.getNodeValue().equals(TRUE_STRING));
        } else if (attributeName.equals(IParserTags.DIALOG)) {
          item.setDialog(attribute.getNodeValue().equals(TRUE_STRING));
        } else {
          AbstractItemExtensionElement[] ie = handleUnknownItemAttribute(attribute, itemNode);
          if (ie != null) {
            itemExtensionElements.add(ie);
          } else {
            String message = NLS.bind(Messages.WARNING_PARSING_UNKNOWN_ATTRIBUTE, (new Object[] {attributeName, itemNode.getNodeName()}));
            addStatus(IStatus.WARNING, message, null);
          }
        }
      }
    }

    if(!title) {
      String message = NLS.bind(Messages.ERROR_PARSING_NO_TITLE, (new Object[] {itemNode.getNodeName()}));
      throw new CheatSheetParserException(message);
    }

    if (itemExtensionElements != null)
      item.setItemExtensions(itemExtensionElements);
  }

  private void handlePerformWhen(IPerformWhenItem item, Node performWhenNode) throws CheatSheetParserException {
    Assert.isNotNull(item);
    Assert.isNotNull(performWhenNode);
    Assert.isTrue(performWhenNode.getNodeName().equals(IParserTags.PERFORMWHEN));

    PerformWhen performWhen = new PerformWhen();

     boolean condition = false;

    // Handle attributes
    NamedNodeMap attributes = performWhenNode.getAttributes();
    if (attributes != null) {
      for (int x = 0; x < attributes.getLength(); x++) {
        Node attribute = attributes.item(x);
        String attributeName = attribute.getNodeName();
        if (attribute == null || attributeName == null)
          continue;

        if (attributeName.equals(IParserTags.CONDITION)) {
          condition = true;
          performWhen.setCondition(attribute.getNodeValue());
        } else {
          String message = NLS.bind(Messages.WARNING_PARSING_UNKNOWN_ATTRIBUTE, (new Object[] {attributeName, performWhenNode.getNodeName()}));
          addStatus(IStatus.WARNING,message, null);
        }
      }
    }

    if(!condition) {
      String message = NLS.bind(Messages.ERROR_PARSING_NO_CONDITION, (new Object[] {performWhenNode.getNodeName()}));
      throw new CheatSheetParserException(message);
    }

    boolean exeutable = false;

    // Handle nodes
    NodeList nodes = performWhenNode.getChildNodes();
    for (int i = 0; i < nodes.getLength(); i++) {
      Node node = nodes.item(i);
      if(node.getNodeName().equals(IParserTags.ACTION)) {
        exeutable = true;
        handleExecutable(performWhen, node, new Action());
      } else if(node.getNodeName().equals(IParserTags.COMMAND)) {
        exeutable = true;
        handleExecutable(performWhen, node, new CheatSheetCommand());
      } else {
        if(node.getNodeType() != Node.TEXT_NODE && node.getNodeType() != Node.COMMENT_NODE ) {
          String message = NLS.bind(Messages.WARNING_PARSING_UNKNOWN_ELEMENT, (new Object[] {node.getNodeName(), performWhenNode .getNodeName()}));
          addStatus(IStatus.WARNING, message, null);
        }
      }
    }

    if(!exeutable) {
      String message = NLS.bind(Messages.ERROR_PARSING_NO_ACTION, (new Object[] {performWhenNode.getNodeName()}));
      throw new CheatSheetParserException(message);
    }

    item.setPerformWhen(performWhen);
  }

  private void handleRepeatedSubItem(Item item, Node repeatedSubItemNode) throws CheatSheetParserException {
    Assert.isNotNull(item);
    Assert.isNotNull(repeatedSubItemNode);
    Assert.isTrue(repeatedSubItemNode.getNodeName().equals(IParserTags.REPEATEDSUBITM));

    RepeatedSubItem repeatedSubItem = new RepeatedSubItem();

    boolean values = false;

    // Handle attributes
    NamedNodeMap attributes = repeatedSubItemNode.getAttributes();
    if (attributes != null) {
      for (int x = 0; x < attributes.getLength(); x++) {
        Node attribute = attributes.item(x);
        String attributeName = attribute.getNodeName();
        if (attribute == null || attributeName == null)
          continue;

        if (attributeName.equals(IParserTags.VALUES)) {
          values = true;
          repeatedSubItem.setValues(attribute.getNodeValue());
        } else {
          String message = NLS.bind(Messages.WARNING_PARSING_UNKNOWN_ATTRIBUTE, (new Object[] {attributeName, repeatedSubItemNode.getNodeName()}));
          addStatus(IStatus.WARNING, message, null);
        }
      }
    }

    if(!values) {
      String message = NLS.bind(Messages.ERROR_PARSING_NO_VALUES, (new Object[] {repeatedSubItemNode.getNodeName()}));
      throw new CheatSheetParserException(message);
    }

    boolean subitem = false;

    // Handle nodes
    NodeList nodes = repeatedSubItemNode.getChildNodes();
    for (int i = 0; i < nodes.getLength(); i++) {
      Node node = nodes.item(i);

      if(node.getNodeName().equals(IParserTags.SUBITEM)) {
        subitem = true;
        handleSubItem(repeatedSubItem, node);
      } else {
        if(node.getNodeType() != Node.TEXT_NODE && node.getNodeType() != Node.COMMENT_NODE ) {
          String message = NLS.bind(Messages.WARNING_PARSING_UNKNOWN_ELEMENT, (new Object[] {node.getNodeName(), repeatedSubItemNode.getNodeName()}));
          addStatus(IStatus.WARNING, message, null);
        }
      }
    }

    if(!subitem) {
      String message = NLS.bind(Messages.ERROR_PARSING_NO_SUBITEM, (new Object[] {repeatedSubItemNode.getNodeName()}));
      throw new CheatSheetParserException(message);
    }

    item.addSubItem(repeatedSubItem);
  }

  private void handleSubItem(ISubItemItem item, Node subItemNode) throws CheatSheetParserException {
    Assert.isNotNull(item);
    Assert.isNotNull(subItemNode);
    Assert.isTrue(subItemNode.getNodeName().equals(IParserTags.SUBITEM));

    SubItem subItem = new SubItem();

    handleSubItemAttributes(subItem, subItemNode);
   
    IncompatibleSiblingChecker checker = new IncompatibleSiblingChecker(this, subItemNode);

    NodeList nodes = subItemNode.getChildNodes();
    for (int i = 0; i < nodes.getLength(); i++) {
      Node node = nodes.item(i);
      checker.checkElement(node.getNodeName());

      if(node.getNodeName().equals(IParserTags.ACTION)) {
        handleExecutable(subItem, node, new Action());
      } else if(node.getNodeName().equals(IParserTags.COMMAND)) {
        handleExecutable(subItem, node, new CheatSheetCommand());
      } else if(node.getNodeName().equals(IParserTags.PERFORMWHEN)) {
        handlePerformWhen(subItem, node);
      } else {
        if(node.getNodeType() != Node.TEXT_NODE && node.getNodeType() != Node.COMMENT_NODE ) {
          String message = NLS.bind(Messages.WARNING_PARSING_UNKNOWN_ELEMENT, (new Object[] {node.getNodeName(), subItemNode.getNodeName()}));
          addStatus(IStatus.WARNING, message, null);
        }
      }
    }
    item.addSubItem(subItem);
  }

  private void handleSubItemAttributes(SubItem subItem, Node subItemNode) throws CheatSheetParserException {
    Assert.isNotNull(subItem);
    Assert.isNotNull(subItemNode);

    boolean label = false;

    NamedNodeMap attributes = subItemNode.getAttributes();
    if (attributes != null) {
      for (int x = 0; x < attributes.getLength(); x++) {
        Node attribute = attributes.item(x);
        String attributeName = attribute.getNodeName();
        if (attribute == null || attributeName == null)
          continue;

        if (attributeName.equals(IParserTags.LABEL)) {
          label = true;
          subItem.setLabel(attribute.getNodeValue());
        } else if (attributeName.equals(IParserTags.SKIP)) {
          subItem.setSkip(attribute.getNodeValue().equals(TRUE_STRING));
        } else if (attributeName.equals(IParserTags.WHEN)) {
          subItem.setWhen(attribute.getNodeValue());
        } else {
          String message = NLS.bind(Messages.WARNING_PARSING_UNKNOWN_ATTRIBUTE, (new Object[] {attributeName, subItemNode.getNodeName()}));
          addStatus(IStatus.WARNING, message, null);
        }
      }
    }

    if(!label) {
      String message = NLS.bind(Messages.ERROR_PARSING_NO_LABEL, (new Object[] {subItemNode.getNodeName()}));
      throw new CheatSheetParserException(message);
    }
  }

  private AbstractItemExtensionElement[] handleUnknownItemAttribute(Node item, Node node) {
    ArrayList al = new ArrayList();
    if (itemExtensionContainerList == null)
      return null;

    for (int i = 0; i < itemExtensionContainerList.size(); i++) {
      CheatSheetItemExtensionElement itemExtensionElement = (CheatSheetItemExtensionElement) itemExtensionContainerList.get(i);

      if (itemExtensionElement.getItemAttribute().equals(item.getNodeName())) {
        AbstractItemExtensionElement itemElement = itemExtensionElement.createInstance();
        if(itemElement != null) {
          itemElement.handleAttribute(item.getNodeValue());
          al.add(itemElement);
        }
      }
    }

    if(al.size() == 0) {
      String message = NLS.bind(Messages.WARNING_PARSING_UNKNOWN_ATTRIBUTE, (new Object[] {item.getNodeName(), node.getNodeName()}));
      addStatus(IStatus.WARNING, message, null);
    }
    return (AbstractItemExtensionElement[])al.toArray(new AbstractItemExtensionElement[al.size()]);
  }

  public ICheatSheet parse(URL url, String pluginId, int cheatSheetKind) {
    return parse(new ParserInput(url, pluginId), cheatSheetKind);
  }
 
  public ICheatSheet parse(ParserInput input, int cheatSheetKind) {
    status = Status.OK_STATUS;
    commandCount = 0;
    actionCount = 0;
    if(input == null) {
      return null;
    }

    InputStream is = null;
    InputSource inputSource = null;
        String filename = ""; //$NON-NLS-1$
    URL url = input.getUrl();

    if (input.getXml() != null) {
      StringReader reader = new StringReader(input.getXml());
      inputSource = new InputSource(reader);
    } else if (input.getUrl() != null){
      try {
        is = url.openStream();
 
        if (is != null) {
          inputSource = new InputSource(is);
        }
      } catch (Exception e) {
        String message = NLS.bind(Messages.ERROR_OPENING_FILE, (new Object[] {url.getFile()}));
        addStatus(IStatus.ERROR, message, e);
        return null;
      }
    } else {
      return null;
    }
   
    if (input.getUrl() != null){
      filename = url.getFile();
    }

    Document document;
    try {
      if(documentBuilder == null) {
        addStatus(IStatus.ERROR, Messages.ERROR_DOCUMENT_BUILDER_NOT_INIT, null);
        return null;
      }
      document = documentBuilder.parse(inputSource);
    } catch (IOException e) {
      String message = NLS.bind(Messages.ERROR_OPENING_FILE_IN_PARSER, (new Object[] {filename}));
      addStatus(IStatus.ERROR, message, e);
      return null;
    } catch (SAXParseException spe) {
      String message = NLS.bind(Messages.ERROR_SAX_PARSING_WITH_LOCATION, (new Object[] {filename, new Integer(spe.getLineNumber()), new Integer(spe.getColumnNumber())}));
      addStatus(IStatus.ERROR, message, spe);
      return null;
    } catch (SAXException se) {
      String message = NLS.bind(Messages.ERROR_SAX_PARSING, (new Object[] {filename}));
      addStatus(IStatus.ERROR, message, se);
      return null;
    } finally {
      try {
        is.close();
      } catch (Exception e) {
      }
    }

    // process dynamic content, normalize paths
    if (processor == null) {
      DocumentReader reader = new DocumentReader();
      processor = new DocumentProcessor(new ProcessorHandler[] {
        new FilterHandler(CheatSheetEvaluationContext.getContext()),
        new NormalizeHandler(),
        new IncludeHandler(reader, Platform.getNL()),
        new ExtensionHandler(reader, Platform.getNL())
      });
    }
    String documentPath = null;
    if (input.getPluginId() != null) {
      documentPath = '/' + input.getPluginId() + input.getUrl().getPath();
    }
    processor.process(UAElementFactory.newElement(document.getDocumentElement()), documentPath);
   
    if ( cheatSheetKind == COMPOSITE_ONLY  ||  (cheatSheetKind == ANY && isComposite(document))) {
      CompositeCheatSheetParser compositeParser = new CompositeCheatSheetParser();
      CompositeCheatSheetModel result = compositeParser.parseCompositeCheatSheet(document, input.getUrl());
      status = compositeParser.getStatus();
      return result;
    }
    try {
      return parseCheatSheet(document);
    } catch(CheatSheetParserException e) {
      addStatus(IStatus.ERROR, e.getMessage(), e);
    }
    return null;
  }

  private boolean isComposite(Document document) {
    if (document != null) {
      Node rootnode = document.getDocumentElement();   
      // Is the root node compositeCheatsheet?
      return rootnode.getNodeName().equals(ICompositeCheatsheetTags.COMPOSITE_CHEATSHEET) ;
    }
    return false;
  }

  private CheatSheet parseCheatSheet(Document document) throws CheatSheetParserException {
    // If the document passed is null return a null tree and update the status
    if (document != null) {
      Node rootnode = document.getDocumentElement();
     
      // Is the root node really <cheatsheet>?
      if( !rootnode.getNodeName().equals(IParserTags.CHEATSHEET) ) {
        throw new CheatSheetParserException(Messages.ERROR_PARSING_CHEATSHEET_ELEMENT);
      }

      // Create the cheat sheet model object
      CheatSheet cheatSheet = new CheatSheet();

      handleCheatSheetAttributes(cheatSheet, rootnode);

      boolean hasItem = false;
      boolean hasIntro = false;
     
      CheatSheetRegistryReader reader = CheatSheetRegistryReader.getInstance();
      itemExtensionContainerList = reader.readItemExtensions();
     
      NodeList nodes = rootnode.getChildNodes();
      for (int i = 0; i < nodes.getLength(); i++) {
        Node node = nodes.item(i);

        if(node.getNodeName().equals(IParserTags.ITEM)) {
          hasItem = true;
          Item item = handleItem(node);
          cheatSheet.addItem(item);
        } else if(node.getNodeName().equals(IParserTags.INTRO)) {
          if (hasIntro) {
            addStatus(IStatus.ERROR, Messages.ERROR_PARSING_MORE_THAN_ONE_INTRO, null);
          } else {
              hasIntro = true;
              handleIntroNode(cheatSheet, node);
          }
        } else {
          if(node.getNodeType() != Node.TEXT_NODE && node.getNodeType() != Node.COMMENT_NODE ) {
            String message = NLS.bind(Messages.WARNING_PARSING_UNKNOWN_ELEMENT, (new Object[] {node.getNodeName(), rootnode.getNodeName()}));
            addStatus(IStatus.WARNING, message, null);
          }
        }
      }
     
      if(!hasIntro) {
        addStatus(IStatus.ERROR, Messages.ERROR_PARSING_NO_INTRO, null);
      }
      if(!hasItem) {
        addStatus(IStatus.ERROR, Messages.ERROR_PARSING_NO_ITEM, null);
      }

      //handleIntro(cheatSheet, document);

      //handleItems(cheatSheet, document);
     
      if (status.getSeverity() == IStatus.ERROR) {
        return null;
      }
     
      cheatSheet.setContainsCommandOrAction(actionCount != 0 || commandCount != 0);
      return cheatSheet;
    }
    throw new CheatSheetParserException(Messages.ERROR_PARSING_CHEATSHEET_CONTENTS);
  }

  /*
   * Normalizes composite cheat sheet-relative paths to simple cheat sheets into fully
   * qualified paths, e.g. for the path "tasks/mySimpleCheatSheet.xml" in composite cheat
   * sheet "/my.plugin/cheatsheets/myCompositeCheatSheet.xml", this normalizes to
   * "/my.plugin/cheatsheets/tasks/mySimpleCheatSheet.xml".
   *
   * This is necessary because with dynamic content we are pulling in tasks from other
   * plug-ins and those tasks have relative paths. It also only applies for cheat sheets
   * located in running plug-ins.
   */
  private class NormalizeHandler extends ProcessorHandler {
   
    private static final String ELEMENT_PARAM = "param"; //$NON-NLS-1$
    private static final String ATTRIBUTE_NAME = "name"; //$NON-NLS-1$
    private static final String ATTRIBUTE_VALUE = "value"; //$NON-NLS-1$
    private static final String NAME_PATH = "path"; //$NON-NLS-1$
   
    public short handle(UAElement element, String id) {
      if (id != null && ELEMENT_PARAM.equals(element.getElementName())) {
        String name = element.getAttribute(ATTRIBUTE_NAME);
        if (NAME_PATH.equals(name)) {
          String value = element.getAttribute(ATTRIBUTE_VALUE);
          if (value != null) {
            int index = id.lastIndexOf('/');
            element.setAttribute(ATTRIBUTE_VALUE, id.substring(0, index + 1) + value);
          }
        }
        return HANDLED_CONTINUE;
      }
      return UNHANDLED;
    }
  }
}
TOP

Related Classes of org.eclipse.ui.internal.cheatsheets.data.CheatSheetParser

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.