All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.jangaroo.exml.parser.ExmlMetadataHandler Maven / Gradle / Ivy

There is a newer version: 4.1.0-alpha-8
Show newest version
package net.jangaroo.exml.parser;

import net.jangaroo.exml.api.ExmlcException;
import net.jangaroo.exml.compiler.Exmlc;
import net.jangaroo.jooc.json.JsonObject;
import net.jangaroo.exml.model.AnnotationAt;
import net.jangaroo.exml.model.ConfigAttribute;
import net.jangaroo.exml.model.ConfigClass;
import net.jangaroo.exml.model.ConfigClassType;
import net.jangaroo.exml.model.DescriptionHolder;
import net.jangaroo.exml.model.Declaration;
import net.jangaroo.exml.model.PublicApiMode;
import net.jangaroo.exml.utils.ExmlUtils;
import net.jangaroo.jooc.Jooc;
import net.jangaroo.utils.CharacterRecordingHandler;
import org.xml.sax.Attributes;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;

import javax.xml.namespace.QName;
import java.util.Deque;
import java.util.LinkedList;

/**
 * Generates an internal representation of all metadata of the component described by the given EXML.
 */
public class ExmlMetadataHandler extends CharacterRecordingHandler {
  private ConfigClass configClass;
  private Locator locator;

  private Deque elementPath = new LinkedList();

  public ExmlMetadataHandler(ConfigClass configClass) {
    this.configClass = configClass;
  }

  @Override
  public void setDocumentLocator(Locator locator) {
    this.locator = locator;
  }

  public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
    if (ExmlUtils.isExmlNamespace(uri)) {
      if (Exmlc.EXML_ROOT_NODE_NAMES.contains(localName)) {
        configClass.setType(ConfigClassType.fromExmlRootNodeName(localName));

        for (int i = 0; i < atts.getLength(); i++) {
          //baseClass attribute has been specified, so the super class of the component is actually that
          if (Exmlc.EXML_PUBLIC_API_ATTRIBUTE.equals(atts.getLocalName(i))) {
            PublicApiMode publicApiMode = Exmlc.parsePublicApiMode(atts.getValue(i));
            if (publicApiMode != PublicApiMode.FALSE) {
              configClass.addAnnotation(Jooc.PUBLIC_API_INCLUSION_ANNOTATION_NAME);
            }
          }
        }
      } else if (Exmlc.EXML_ANNOTATION_NODE_NAME.equals(localName)) {
        AnnotationAt annotationAt = AnnotationAt.BOTH; // default for "at" is "both"
        for (int i = 0; i < atts.getLength(); i++) {
          if (Exmlc.EXML_ANNOTATION_AT_ATTRIBUTE.equals(atts.getLocalName(i))) {
            // found "at" attribute: parse it (might throw ExmlcException)
            annotationAt = Exmlc.parseAnnotationAtValue(atts.getValue(i));
            break;
          }
        }
        if (annotationAt != AnnotationAt.TARGET) {
          startRecordingCharacters();
        }
      } else if (Exmlc.EXML_CFG_NODE_NAME.equals(localName)) {
        //handle config elements
        ConfigAttribute cfg = new ConfigAttribute(atts.getValue(Exmlc.EXML_CFG_NAME_ATTRIBUTE), atts.getValue(Exmlc.EXML_CFG_TYPE_ATTRIBUTE), null);
        if(!configClass.contains(cfg)) {
          configClass.addCfg(cfg);
        } else {
          throw new ExmlcException("Config '" + cfg.getName() + "' already defined.", locator.getLineNumber(), locator.getColumnNumber());
        }
      } else if (Exmlc.EXML_DESCRIPTION_NODE_NAME.equals(localName)) {
        if (isLastInPathExmlClass() || isLastInPathConfig() || isLastInPathConstant()) {
          // start recording characters of the description:
          startRecordingCharacters();
        }
      } else if (Exmlc.EXML_DECLARATION_VALUE_NODE_NAME.equals(localName)) {
        if (isLastInPathConstant()) {
          // start recording characters of the constant value:
          startRecordingCharacters();
        }
      } else if (Exmlc.EXML_CONSTANT_NODE_NAME.equals(localName)) {
        final String name = atts.getValue(Exmlc.EXML_DECLARATION_NAME_ATTRIBUTE);
        final String type = atts.getValue(Exmlc.EXML_DECLARATION_TYPE_ATTRIBUTE);
        final String value = atts.getValue(Exmlc.EXML_DECLARATION_VALUE_ATTRIBUTE);
        Declaration constant = new Declaration(name, formatValue(value, type), type);
        if(!configClass.getConstants().contains(constant)) {
          configClass.addConstant(constant);
        } else {
          throw new ExmlcException("Constant '" + constant.getName() + "' already defined.", locator.getLineNumber(), locator.getColumnNumber());
        }
      } else if (Exmlc.EXML_IMPORT_NODE_NAME.equals(localName)) {
        String importedClassName = atts.getValue(Exmlc.EXML_IMPORT_CLASS_ATTRIBUTE);
        if (importedClassName != null) {
          // TODO: check illegal values? Throw error when null?
          configClass.addImport(importedClassName);
        }
      }
    } else if (elementPath.size() == 1) {
      if (configClass.getSuperClassName() != null) {
        throw new ExmlcException("root node of EXML contained more than one component definition", locator.getLineNumber(), locator.getColumnNumber());
      }

      String thePackage = ExmlUtils.parsePackageFromNamespace(uri);
      if (thePackage == null) {
        throw new ExmlcException("namespace '" + uri + "' of superclass element in EXML file does not denote a config package", locator.getLineNumber(), locator.getColumnNumber());
      }
      configClass.setSuperClassName(thePackage + "." + localName);
    }
    elementPath.push(new QName(uri, localName));
  }

  private static String formatValue(String value, String type) {
    return value == null ? null
            : JsonObject.valueToString(ExmlToModelParser.getAttributeValue(value, type), 2, 4);
  }

  private boolean isLastInPathExmlClass() {
    QName parent = elementPath.peek();
    return ExmlUtils.isExmlNamespace(parent.getNamespaceURI()) && Exmlc.EXML_ROOT_NODE_NAMES.contains(parent.getLocalPart());
  }

  private boolean isLastInPathConfig() {
    QName parent = elementPath.peek();
    return ExmlUtils.isExmlNamespace(parent.getNamespaceURI()) && Exmlc.EXML_CFG_NODE_NAME.equals(parent.getLocalPart());
  }

  private boolean isLastInPathConstant() {
    QName parent = elementPath.peek();
    return ExmlUtils.isExmlNamespace(parent.getNamespaceURI()) && Exmlc.EXML_CONSTANT_NODE_NAME.equals(parent.getLocalPart());
  }

  @Override
  public void endElement(String uri, String localName, String qName) throws SAXException {
    elementPath.pop();
    if (ExmlUtils.isExmlNamespace(uri)) {
      if (Exmlc.EXML_DESCRIPTION_NODE_NAME.equals(localName)) {
        String characters = popRecordedCharacters();
        if (characters != null) {
          DescriptionHolder descriptionHolder =
                  isLastInPathConfig() ? configClass.getCfgs().get(configClass.getCfgs().size() - 1)
                          : isLastInPathExmlClass() ? configClass
                          : isLastInPathConstant() ? getLastConstantDeclaration()
                          : null;
          if (descriptionHolder != null) {
            descriptionHolder.setDescription(characters);
          }
        }
      } else if (Exmlc.EXML_DECLARATION_VALUE_NODE_NAME.equals(localName)) {
        String characters = popRecordedCharacters();
        if (characters != null) {
          final Declaration lastConstantDeclaration = getLastConstantDeclaration();
          lastConstantDeclaration.setValue(formatValue(characters, lastConstantDeclaration.getType()));
        }
      } else if (Exmlc.EXML_ANNOTATION_NODE_NAME.equals(localName)) {
        String characters = popRecordedCharacters();
        if (characters != null) {
          configClass.addAnnotation(characters);
        }
      }
      if (elementPath.isEmpty() && configClass.getSuperClassName() == null) {
        // if nothing else is specified, extend default config class depending on the config class type:
        configClass.setSuperClassName(configClass.getType().getDefaultSuperConfigClassName());
      }
    }
  }

  private Declaration getLastConstantDeclaration() {
    return configClass.getConstants().get(configClass.getConstants().size() - 1);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy