Package org.molgenis.model

Source Code of org.molgenis.model.MolgenisModelParser

/**
* File: invengine_generate/parser/XmlParser.java <br>
* Copyright: Inventory 2000-2006, GBIC 2005, all rights reserved <br>
* Changelog:
* <ul>
* <li>2005-12-08; 1.0.0; RA Scheltema Creation.
* </ul>
*/
package org.molgenis.model;

// jdk
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.StringTokenizer;
import java.util.Vector;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.log4j.Logger;
import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer;
import org.molgenis.MolgenisFieldTypes;
import org.molgenis.fieldtypes.UnknownField;
import org.molgenis.model.elements.DBSchema;
import org.molgenis.model.elements.Entity;
import org.molgenis.model.elements.Field;
import org.molgenis.model.elements.Form;
import org.molgenis.model.elements.Form.SortOrder;
import org.molgenis.model.elements.Index;
import org.molgenis.model.elements.Matrix;
import org.molgenis.model.elements.Menu;
import org.molgenis.model.elements.Method;
import org.molgenis.model.elements.MethodQuery;
import org.molgenis.model.elements.Model;
import org.molgenis.model.elements.Module;
import org.molgenis.model.elements.Parameter;
import org.molgenis.model.elements.Plugin;
import org.molgenis.model.elements.Plugin.Flavor;
import org.molgenis.model.elements.Record;
import org.molgenis.model.elements.Tree;
import org.molgenis.model.elements.UISchema;
import org.molgenis.model.elements.View;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
* TODO: refactor: spread over multiple files.
*/
public class MolgenisModelParser
{

  /**
   *
   * @param model
   * @param element
   *            a DOM element that looks like <entity name="a"
   *            abstract="true"><field name="a" type="....
   * @throws MolgenisModelException
   */
  public static Entity parseEntity(Model model, Element element) throws MolgenisModelException
  {
    // check for illegal words
    String[] keywords = new String[]
    { "name", "label", "extends", "implements", "abstract", "description", "system", "decorator", "xref_label",
        "allocationSize" };
    List<String> key_words = new ArrayList<String>(Arrays.asList(keywords));
    for (int i = 0; i < element.getAttributes().getLength(); i++)
    {
      if (!key_words.contains(element.getAttributes().item(i).getNodeName()))
      {
        throw new MolgenisModelException("attribute '" + element.getAttributes().item(i).getNodeName()
            + "' not allowed for <entity>");
      }
    }

    // check properties
    // NAME
    if (element.getAttribute("name").trim().isEmpty())
    {
      String message = "name is missing for entity " + element.toString();
      logger.error(message);
      throw new MolgenisModelException(message);
    }

    // construct
    Entity entity = new Entity(element.getAttribute("name").trim(), element.getAttribute("label"),
        model.getDatabase());
    entity.setNamespace(model.getName());

    // add optional properties
    // EXTENDS exactly one
    String _extends = element.getAttribute("extends");
    if (_extends != null)
    {
      Vector<String> parents = new Vector<String>();
      StringTokenizer tokenizer = new StringTokenizer(_extends, ",");
      while (tokenizer.hasMoreTokens())
      {
        parents.add(tokenizer.nextToken().trim());
      }

      entity.setParents(parents);
    }

    // IMPLEMENTS
    String _implements = element.getAttribute("implements");
    if (_implements != null && !_implements.isEmpty())
    {
      entity.setImplements(new Vector<String>(Arrays.asList(_implements.split(","))));
    }

    // ABSTRACT
    entity.setAbstract(Boolean.parseBoolean(element.getAttribute("abstract")));

    // SYSTEM
    entity.setSystem(Boolean.parseBoolean(element.getAttribute("system")));

    // XREF_LABEL
    String xref_label = element.getAttribute("xref_label");
    if (xref_label != null && !xref_label.isEmpty())
    {
      List<String> xref_labels = new ArrayList<String>();
      xref_labels.addAll(Arrays.asList(xref_label.split(",")));
      entity.setXrefLabels(xref_labels);
    }
    else
    {
      entity.setXrefLabels(null);
    }

    // TRIGGER
    if (element.hasAttribute("decorator"))
    {
      entity.setDecorator(element.getAttribute("decorator"));
    }

    // DESCRIPTION
    NodeList elements = element.getElementsByTagName("description");
    for (int j = 0; j < elements.getLength(); j++)
    {
      // parse the contents, including markup...
      entity.setDescription(elementValueToString((Element) elements.item(j)));
    }

    // FIELD
    elements = element.getElementsByTagName("field");
    for (int j = 0; j < elements.getLength(); j++)
    {
      Element elem = (Element) elements.item(j);
      parseField(entity, elem);
    }

    // UNIQUE
    elements = element.getElementsByTagName("unique");
    for (int j = 0; j < elements.getLength(); j++)
    {
      Element elem = (Element) elements.item(j);
      Vector<String> keys = new Vector<String>();

      // keys from keyfield="a,b" attribute
      if (elem.hasAttribute("fields"))
      {
        for (String name : elem.getAttribute("fields").split(","))
        {
          // Field f = entity.getField(name);
          // if (f == null)
          // {
          // // try to get superclass field (need to copy it then)
          // f = entity.getAllField(name);
          // if (f == null) throw new
          // MolgenisModelException("Missing unique field '" + name
          // + "' in entity '" + entity.getName() + "'");
          //
          // // copy the field so it will end up in the table (as
          // // redundant copy) to enforce unique
          // // f = new Field(f);
          // // f.setEntity(entity);
          // // f.setSystem(true);
          // // entity.addField(f);
          // }

          keys.add(name);
        }

      }

      // keys from <keyfield> elements
      NodeList key_elements = elem.getElementsByTagName("keyfield");
      for (int k = 0; k < key_elements.getLength(); k++)
      {
        elem = (Element) key_elements.item(k);

        String name = elem.getAttribute("name");
        // should include superclass methods now
        // if (f == null)
        // {
        // throw new MolgenisModelException("Missing unique field: " +
        // elem.getAttribute("name"));
        // // return null;
        // }

        keys.add(name);
      }

      // description
      String key_description = null;
      if (elem.hasAttribute("description"))
      {
        key_description = elem.getAttribute("description");
      }

      // check if keys
      if (keys.size() == 0)
      {
        throw new MolgenisModelException("missing fields on unique of '" + entity.getName()
            + "'. Expected <unique fields=\"field1[,field2,..]\" description=\"...\"/>");
      }

      try
      {
        entity.addKey(keys, elem.getAttribute("subclass").equals("true"), key_description);
        // might be duplicate key
      }
      catch (Exception e)
      {
        throw new MolgenisModelException(e.getMessage());
      }
    }

    elements = element.getElementsByTagName("indices");
    if (elements.getLength() == 1)
    {
      Element elem = (Element) elements.item(0);

      NodeList index_elements = elem.getElementsByTagName("index");
      for (int k = 0; k < index_elements.getLength(); k++)
      {
        elem = (Element) index_elements.item(k);

        Index index = new Index(elem.getAttribute("name"));

        NodeList indexfield_elements = elem.getElementsByTagName("indexfield");
        for (int l = 0; l < indexfield_elements.getLength(); l++)
        {
          elem = (Element) indexfield_elements.item(l);

          Field f = entity.getField(elem.getAttribute("name"));
          if (f == null)
          {
            // System.err.println(String.format(Error.
            // MISSING_INDEX_FIELD.msg,
            // elem.getAttribute("name")));
            throw new MolgenisModelException("Missing index field: " + elem.getAttribute("name"));
            // return null;
          }

          try
          {
            index.addField(elem.getAttribute("name"));
          }
          catch (Exception e)
          {
            throw new MolgenisModelException(e.getMessage());
          }
        }

        try
        {
          entity.addIndex(index);
        }
        catch (Exception e)
        {
        }
      }
    }
    else if (elements.getLength() > 1)
    {
      // System.err.println(Error.MULTIPLE_INDICES_ELEMENTS.msg);
      // return null;
      throw new MolgenisModelException("Multiple indices elements");
    }
    // done

    // Todo: change if(molgenisOptions.jpa_use_sequence &&
    // element.hasAttribute("allocationSize"))
    if (element.hasAttribute("allocationSize"))
    {
      int allocationSize = Integer.parseInt(element.getAttribute("allocationSize"));
      entity.setAllocationSize(allocationSize);
    }

    logger.debug("read: " + entity.getName());
    return entity;
  }

  public static void parseMatrix(Model model, Element element) throws MolgenisModelException
  {
    // check parameters
    String[] keywords = new String[]
    { "name", "content_entity", "content", "container", "row", "col", "row_entity", "col_entity" };
    List<String> key_words = new ArrayList<String>(Arrays.asList(keywords));
    for (int i = 0; i < element.getAttributes().getLength(); i++)
    {
      if (!key_words.contains(element.getAttributes().item(i).getNodeName()))
      {
        throw new MolgenisModelException("attribute '" + element.getAttributes().item(i).getNodeName()
            + "' unknown for <entity>");
      }
    }

    // make sure required properties are set
    if (element.getAttribute("name") != null && element.getAttribute("name").isEmpty())
    {
      String message = "name is missing for entity " + element.toString();
      logger.error(message);
      throw new MolgenisModelException(message);
    }

    // create the matrix object and put it in the model
    Matrix matrix = new Matrix(element.getAttribute("name"), model.getDatabase());
    matrix.setCol(element.getAttribute("col"));
    matrix.setRow(element.getAttribute("row"));
    matrix.setContentEntity(element.getAttribute("content_entity"));
    matrix.setContainer(element.getAttribute("container"));
    matrix.setColEntityName(element.getAttribute("col_entity"));
    matrix.setRowEntityName(element.getAttribute("row_entity"));
    matrix.setContent(element.getAttribute("content"));

    logger.debug("read: " + matrix.toString());
  }

  public static void parseField(Entity entity, Element element) throws MolgenisModelException
  {
    // check for illegal words
    String[] keywords = new String[]
    { "type", "name", "label", "auto", "nillable", "optional", "readonly", "default", "description", "desc",
        "unique", "hidden", "length", "index", "enum_options", "default_code", "xref", "xref_entity",
        "xref_field", "xref_label", "xref_name", "mref_name", "mref_localid", "mref_remoteid", "filter",
        "filtertype", "filterfield", "filtervalue", "xref_cascade" + "", "allocationSize", "jpaCascade" };
    List<String> key_words = new ArrayList<String>(Arrays.asList(keywords));
    for (int i = 0; i < element.getAttributes().getLength(); i++)
    {
      if (!key_words.contains(element.getAttributes().item(i).getNodeName()))
      {
        throw new MolgenisModelException("attribute '" + element.getAttributes().item(i).getNodeName()
            + "' not allowed for <field>");
      }
    }

    // required properties
    String type = element.getAttribute("type");
    String name = element.getAttribute("name");
    String label = element.getAttribute("label");
    String auto = element.getAttribute("auto");
    String nillable = element.getAttribute("nillable");
    if (element.hasAttribute("optional"))
    {
      nillable = element.getAttribute("optional");
    }
    String readonly = element.getAttribute("readonly");
    String default_value = element.getAttribute("default");

    // other properties
    String description = element.getAttribute("description");
    if (description.isEmpty())
    {
      description = element.getAttribute("desc");
    }
    String unique = element.getAttribute("unique");
    String hidden = element.getAttribute("hidden");
    String length = element.getAttribute("length");
    String index = element.getAttribute("index");
    String enum_options = element.getAttribute("enum_options").replace('[', ' ').replace(']', ' ').trim();
    String default_code = element.getAttribute("default_code");
    // xref and mref
    String xref_entity = element.getAttribute("xref_entity");
    String xref_field = element.getAttribute("xref_field");
    String xref_label = element.getAttribute("xref_label");
    String mref_name = element.getAttribute("mref_name");
    String mref_localid = element.getAttribute("mref_localid");
    String mref_remoteid = element.getAttribute("mref_remoteid");

    if (type.isEmpty())
    {
      type = "string";
    }

    if (element.hasAttribute("xref") && !element.hasAttribute("xref_field"))
    {
      xref_field = element.getAttribute("xref");
    }

    if (!xref_field.isEmpty() && !element.hasAttribute("xref_entity"))
    {
      String[] entity_field = xref_field.split("[.]");

      if (entity_field.length == 2)
      {
        xref_entity = entity_field[0];
        xref_field = entity_field[1];
      }
    }

    // filter for xref and mref
    String filter = element.getAttribute("filter");
    String filtertype = element.getAttribute("filtertype");
    String filterfield = element.getAttribute("filterfield");
    String filtervalue = element.getAttribute("filtervalue");

    // (re)set optional properties
    if (type.equals("varchar"))
    {
      type = "string";
    }
    if (type.equals("number"))
    {
      type = "int";
    }
    if (type.equals("boolean"))
    {
      type = "bool";
    }
    if (type.equals("xref_single"))
    {
      type = "xref";
    }
    if (type.equals("xref_multiple"))
    {
      type = "mref";
    }
    if (label.isEmpty())
    {
      label = name;
    }
    if (description.isEmpty())
    {
      description = label;
    }
    if (xref_label == null || xref_label.isEmpty())
    {
      xref_label = null;
    }
    if (type.equals("autoid"))
    {
      type = "int";
      nillable = "false";
      auto = "true";
      readonly = "true";
      unique = "true";
      default_value = "";
    }

    // TODO: validate the booleans to "true" or "false" (or even better: if
    // the value is set, than it is true).

    // check exceptions
    if (type != null && type.isEmpty())
    {
      throw new MolgenisModelException("type is missing for field '" + name + "' of entity '" + entity.getName()
          + "'");
    }
    if (MolgenisFieldTypes.getType(type) instanceof UnknownField)
    {
      throw new MolgenisModelException("type '" + type + "' unknown for field '" + name + "' of entity '"
          + entity.getName() + "'");
    }
    if (name.isEmpty())
    {
      throw new MolgenisModelException("name is missing for a field in entity '" + entity.getName() + "'");
    }
    if (hidden.equals("true") && !nillable.equals("true") && (default_value.isEmpty() && !auto.equals("true")))
    {
      throw new MolgenisModelException("field '" + name + "' of entity '" + entity.getName()
          + "' must have a default value. A field that is not nillable and hidden must have a default value.");
    }

    String jpaCascade = null;
    if (type.equals("mref") || type.equals("xref"))
    {
      if (element.hasAttribute("jpaCascade"))
      {
        jpaCascade = element.getAttribute("jpaCascade");
      }
    }

    // construct
    Field field = new Field(entity, MolgenisFieldTypes.getType(type), name, label, Boolean.parseBoolean(auto),
        Boolean.parseBoolean(nillable), Boolean.parseBoolean(readonly), default_value, jpaCascade);
    logger.debug("read: " + field.toString());

    // add optional properties
    if (!description.isEmpty())
    {
      field.setDescription(description.trim());
    }
    if (hidden.equals("true"))
    {
      field.setHidden(true);
    }
    if (!default_code.isEmpty())
    {
      field.setDefaultCode(default_code);
    }
    if (filter.equals("true"))
    {
      logger.warn("filter set for field '" + name + "' of entity '" + entity.getName() + "'");
      logger.warn(filterfield + " " + filtertype + " " + filtervalue);
      logger.warn(System.currentTimeMillis() + " - filter bool: '" + Boolean.parseBoolean(filter) + "'");
      if ((filtertype != null && filtertype.isEmpty()) || (filterfield != null && filterfield.isEmpty()))
      {
        throw new MolgenisModelException("field '" + name + "' of entity '" + entity.getName()
            + "': when the filter is set to true, the filtertype, filterfield and filtervalue must be set");
      }
      if (filtervalue != null && filtervalue.isEmpty())
      {
        logger.warn("no value specified for filter in field '" + name + "' of entity '" + entity.getName()
            + "'");
      }
      field.setFilter(Boolean.parseBoolean(filter));
      field.setFiltertype(filtertype);
      field.setFilterfield(filterfield);
      field.setFiltervalue(filtervalue);
    }

    // add type dependent properties
    if (type.equals("string"))
    {
      if (!length.isEmpty())
      {
        field.setVarCharLength(Integer.parseInt(length));
      }
      else
      {
        field.setVarCharLength(255);
      }
    }
    else if (type.equals("enum"))
    {
      Vector<String> options = new Vector<String>();
      StringTokenizer tokenizer = new StringTokenizer(enum_options, ",");
      while (tokenizer.hasMoreElements())
      {
        options.add(tokenizer.nextToken().trim());
      }
      if (options.size() < 1)
      {
        throw new MolgenisModelException("enum_options must be ',' delimited for field '" + field.getName()
            + "' of entity '" + entity.getName() + "'");
      }

      field.setEnumOptions(options);
    }
    else if (type.equals("xref") || type.equals("mref"))
    {
      // xref must be defined unless mref_name is set
      // caveat, can be both ends!!!
      if (mref_name.isEmpty() && xref_entity.isEmpty())
      {
        throw new MolgenisModelException("xref_entity must be set for xref field '" + field.getName()
            + "' of entity '" + entity.getName() + "'");
      }

      List<String> xref_labels = null;
      if (xref_label != null)
      {
        xref_labels = Arrays.asList(xref_label.split(","));
      }

      field.setXRefVariables(xref_entity, xref_field, xref_labels);

      // optional custom naming instead of default
      // necessary when using existing database
      if (type.equals("mref"))
      {
        if (!mref_name.isEmpty())
        {
          field.setMrefName(mref_name);
        }
        if (!mref_localid.isEmpty())
        {
          field.setMrefLocalid(mref_localid);
        }
        if (!mref_remoteid.isEmpty())
        {
          field.setMrefRemoteid(mref_remoteid);
        }
      }

      if (!element.getAttribute("xref_cascade").isEmpty())
      {
        if (element.getAttribute("xref_cascade").equalsIgnoreCase("true"))
        {
          field.setXrefCascade(true);
        }
        else
        {
          throw new MolgenisModelException("Unknown option on xref_cascade: '"
              + element.getAttribute("xref_cascade") + "'");
        }
      }
    }

    // add the field to entity
    try
    {
      entity.addField(field);

    }
    catch (Exception e)
    {
      throw new MolgenisModelException("duplicate field '" + field.getName() + "' in entity '" + entity.getName()
          + "'");
    }

    // check whether this field has a short-hand for index
    if (index.equals("true"))
    {
      Index i = new Index(name);
      try
      {
        i.addField(name);
      }
      catch (Exception e)
      {
        throw new MolgenisModelException("duplicate field '" + field.getName() + "' in entity '"
            + entity.getName() + "'");
      }

      entity.addIndex(i);
    }

    // check whether this field has a short-hand for unique
    if (unique.equals("true"))
    {
      entity.addKey(field.getName(), null);
    }

    // parse annotations (used by JPA)
    if (element.getChildNodes().getLength() >= 1)
    {
      String annotations = org.apache.commons.lang.StringUtils.deleteWhitespace(
          element.getChildNodes().item(1).getTextContent()).trim();
      field.setAnnotations(annotations);
    }

  }

  public static void parseView(Model model, Element element) throws MolgenisModelException
  {
    // get the attributes
    String name = element.getAttribute("name");
    String label = element.getAttribute("label");
    String entities = element.getAttribute("entities");

    // check properties
    if (name.isEmpty())
    {
      throw new MolgenisModelException("name is missing for view " + element.toString());
    }
    if (entities.isEmpty())
    {
      throw new MolgenisModelException("entities is missing for view " + element.toString());
    }
    if (label.isEmpty())
    {
      label = name;
    }

    List<String> entityList = new ArrayList<String>(Arrays.asList(entities.split(",")));
    if (entityList.size() < 2)
    {
      throw new MolgenisModelException("a view needs at least 2 entities, define as entities=\"e1,e2\": "
          + element.toString());
    }

    // construct the view
    View view = new View(name, label, model.getDatabase());

    // add the viewentities
    for (String viewentity : entityList)
    {
      if (view.getEntities().contains(viewentity))
      {
        throw new MolgenisModelException("view " + name + " has duplicate viewentity entries (" + viewentity
            + ")");
      }
      view.addEntity(viewentity);
    }
  }

  // Method parser
  public static void parseMethod(Model model, Element element) throws MolgenisModelException
  {
    // NAME
    if (element.getAttribute("name") != null && element.getAttribute("name").isEmpty())
    {
      String message = "name is missing for method " + element.toString();
      logger.error(message);
      throw new MolgenisModelException(message);
    }

    // construct
    Method method = new Method(element.getAttribute("name"), model.getMethodSchema());

    NodeList nodes = element.getChildNodes();
    for (int nodeid = 0; nodeid < nodes.getLength(); ++nodeid)
    {
      Node node = nodes.item(nodeid);
      if (node.getNodeType() != Node.ELEMENT_NODE)
      {
        continue;
      }

      if (((Element) node).getTagName().equals("description"))
      {
        method.setDescription(((Element) node).getTextContent().trim());
      }
      else if (((Element) node).getTagName().equals("parameter"))
      {
        parseParameter(method, (Element) node);
      }
      else if (((Element) node).getTagName().equals("return"))
      {
        parseReturnType(model, method, (Element) node);
      }
      else if (((Element) node).getTagName().equals("query"))
      {
        parseQuery(model, method, (Element) node);
      }
    }
  }

  public static void parseParameter(Method method, Element element) throws MolgenisModelException
  {
    // check properties
    // NAME
    if (element.getAttribute("name") != null && element.getAttribute("name").isEmpty())
    {
      String message = "name is missing for parameter " + element.toString();
      logger.error(message);
      throw new MolgenisModelException(message);
    }
    // TYPE
    if (element.getAttribute("type") != null && element.getAttribute("type").isEmpty())
    {
      String message = "type is missing for parameter " + element.toString();
      logger.error(message);
      throw new MolgenisModelException(message);
    }

    // construct
    Parameter parameter = new Parameter(method, Parameter.Type.getType(element.getAttribute("type")),
        element.getAttribute("name"), element.getAttribute("label"), false, element.getAttribute("default"));

    try
    {
      method.addParameter(parameter);
    }
    catch (Exception e)
    {
      throw new MolgenisModelException("duplicate parameter '" + parameter.getName() + "' in method '"
          + method.getName() + "'");
    }
  }

  public static void parseReturnType(Model model, Method method, Element element) throws MolgenisModelException
  {
    // check properties
    // TYPE
    if (element.getAttribute("type") != null && element.getAttribute("type").isEmpty())
    {
      String message = "type is missing for returntype " + element.toString();
      logger.error(message);
      throw new MolgenisModelException(message);
    }

    // construct
    Entity entity = model.getEntity(element.getAttribute("type"));

    try
    {
      method.setReturnType(entity);
    }
    catch (Exception e)
    {
    }
  }

  public static void parseQuery(Model model, Method method, Element element) throws MolgenisModelException
  {
    // check properties
    // TYPE
    if (element.getAttribute("entity") != null && element.getAttribute("entity").isEmpty())
    {
      String message = "type is missing for returntype " + element.toString();
      logger.error(message);
      throw new MolgenisModelException(message);
    }

    // construct
    MethodQuery query = new MethodQuery(element.getAttribute("entity"));
    method.setQuery(query);

    // parse the rules
    NodeList nodes = element.getChildNodes();
    for (int nodeid = 0; nodeid < nodes.getLength(); ++nodeid)
    {
      Node node = nodes.item(nodeid);
      if (node.getNodeType() != Node.ELEMENT_NODE)
      {
        continue;
      }

      if (((Element) node).getTagName().equals("rule"))
      {
        parseQueryRule(model, query, (Element) node);
      }
    }
  }

  public static void parseQueryRule(Model model, MethodQuery query, Element element) throws MolgenisModelException
  {
    // check properties
    // TYPE
    if (element.getAttribute("field") != null && element.getAttribute("field").isEmpty())
    {
      String message = "type is missing for field " + element.toString();
      logger.error(message);
      throw new MolgenisModelException(message);
    }
    if (element.getAttribute("operator") != null && element.getAttribute("operator").isEmpty())
    {
      String message = "type is missing for operator " + element.toString();
      logger.error(message);
      throw new MolgenisModelException(message);
    }
    if (element.getAttribute("parameter") != null && element.getAttribute("parameter").isEmpty())
    {
      String message = "type is missing for parameter " + element.toString();
      logger.error(message);
      throw new MolgenisModelException(message);
    }

    // construct
    query.addRule(new MethodQuery.Rule(element.getAttribute("field"), element.getAttribute("operator"), element
        .getAttribute("parameter")));
  }

  /** Simple parse an xml string */
  public static Model parseDbSchema(String xml) throws MolgenisModelException
  {
    Model model = new Model("molgenis");

    try
    {
      DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();

      ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes("UTF-8"));

      Document document = builder.parse(is);

      parseXmlDocument(model, document);
    }
    catch (Exception e)
    {
      e.printStackTrace();
      throw new MolgenisModelException(e.getMessage());
    }

    return model;
  }

  // db parser
  public static Model parseDbSchema(ArrayList<String> filenames) throws MolgenisModelException
  {
    Model model = new Model("molgenis");
    Document document = null;

    // initialize the document
    for (String filename : filenames)
    {
      DocumentBuilder builder = null;
      try
      {
        builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
        document = builder.parse(filename.trim());
      }
      catch (ParserConfigurationException e)
      {
        throw new RuntimeException(e);
      }
      catch (Exception e)
      {
        try
        {
          // try to load from classpath
          document = builder.parse(ClassLoader.getSystemResourceAsStream(filename.trim()));
        }
        catch (Exception e2)
        {
          logger.error("parsing of file '" + filename + "' failed.");
          e.printStackTrace();
          throw new MolgenisModelException("Parsing of DSL (schema) failed: " + e.getMessage());
        }
      }

      parseXmlDocument(model, document);
    }

    return model;
  }

  private static Document parseXmlDocument(Model model, Document document) throws MolgenisModelException
  {
    // retrieve the document-root
    Element document_root = document.getDocumentElement();
    if (document_root.getAttribute("name") != null && document_root.getAttribute("name").isEmpty())
    {
      document_root.setAttribute("name", "molgenis");
    }

    String modelName = document_root.getAttribute("name");
    String modelLabel = document_root.getAttribute("label");

    model.setName(modelName);
    if (!"".equals(modelLabel))
    {
      model.setLabel(modelLabel);
    }

    // retrieve the children
    NodeList children = document_root.getChildNodes();

    for (int i = 0; i < children.getLength(); i++)
    {
      Node child = children.item(i);

      if (child.getNodeType() != Node.ELEMENT_NODE)
      {
        continue;
      }

      Element element = (Element) child;
      if (element.getTagName().equals("module"))
      {
        parseModule(model, element);
      }
      else if (element.getTagName().equals("entity"))
      {
        parseEntity(model, element);
      }
      else if (element.getTagName().equals("matrix"))
      {
        parseMatrix(model, element);
      }
      else if (element.getTagName().equals("view"))
      {
        parseView(model, element);
      }
      else if (element.getTagName().equals("method"))
      {
        parseMethod(model, element);
      }
      else if (element.getTagName().equals("description"))
      {
        model.setDBDescription(model.getDBDescription() + elementValueToString(element));
      }
    }
    return document;
  }

  public static void parseModule(Model model, Element element) throws MolgenisModelException
  {
    // check for illegal words
    String[] keywords = new String[]
    { "name", "label" };
    List<String> key_words = new ArrayList<String>(Arrays.asList(keywords));
    for (int i = 0; i < element.getAttributes().getLength(); i++)
    {
      if (!key_words.contains(element.getAttributes().item(i).getNodeName()))
      {
        throw new MolgenisModelException("attribute '" + element.getAttributes().item(i).getNodeName()
            + "' unknown for <module " + element.getAttribute("name") + ">");
      }
    }

    // check properties
    // NAME
    if (element.getAttribute("name").trim().isEmpty())
    {
      String message = "name is missing for module " + element.toString();
      logger.error(message);
      throw new MolgenisModelException(message);
    }

    // construct
    Module module = new Module(model.getName() + "." + element.getAttribute("name").trim(), model);

    if (element.getAttribute("label") != null && !element.getAttribute("label").isEmpty())
    {
      module.setLabel(element.getAttribute("label"));
    }

    // DESCRIPTION
    NodeList elements = element.getElementsByTagName("description");
    for (int j = 0; j < elements.getLength(); j++)
    {
      // parse the contents, including markup...
      if (elements.item(j).getParentNode().equals(element))
      {
        module.setDescription(elementValueToString((Element) elements.item(j)));
      }
    }

    // ENTITY
    elements = element.getElementsByTagName("entity");
    for (int j = 0; j < elements.getLength(); j++)
    {
      Element elem = (Element) elements.item(j);
      Entity e = parseEntity(model, elem);
      e.setNamespace(module.getName());
      module.getEntities().add(e);
      e.setModule(module);
    }
  }

  private static void parseUiSchema(Model model, Element element, UISchema parent) throws MolgenisModelException
  {
    UISchema new_parent = null;

    if (element.getTagName().equals("include"))
    {
      String fileName = element.getAttribute("file");
      if (fileName == null || fileName.isEmpty())
      {
        throw new MolgenisModelException("include failed: no file attribute set");
      }
      try
      {
        Document document_root = parseXmlFile(fileName);
        // include all its elements, ignore the root node
        NodeList rootChildren = document_root.getChildNodes();
        for (int i = 0; i < rootChildren.getLength(); i++)
        {
          NodeList children = rootChildren.item(i).getChildNodes();
          for (int j = 0; j < children.getLength(); j++)
          {
            Node child = children.item(j);
            if (child.getNodeType() != Node.ELEMENT_NODE)
            {
              continue;
            }
            try
            {

              parseUiSchema(model, (Element) child, parent);

            }
            catch (Exception e)
            {
              throw new MolgenisModelException(e.getMessage());
            }

          }
        }

      }
      catch (Exception e)
      {
        throw new MolgenisModelException("include failed: " + e.getMessage());
      }
    }
    else if (element.getTagName().equals("description"))
    {
      logger.warn("currently the '<description>' tag is ignored in ui.xml");
    }
    else
    {

      String name = element.getAttribute("name").trim();
      String namespace = model.getName();
      String label = element.getAttribute("label");
      String group = element.getAttribute("group");
      String groupRead = element.getAttribute("groupRead");

      // check required properties
      if ((name == null || name.isEmpty()) && !element.getTagName().equals("form"))
      {
        throw new MolgenisModelException("name is missing for subform of screen '" + parent.getName() + "'");
      }
      if (label != null && label.isEmpty())
      {
        label = name;
      }

      if (group.isEmpty())
      {
        group = null; // TODO: Discuss with Erik/Morris/Robert!
      }

      if (groupRead.isEmpty())
      {
        groupRead = null; // TODO: Discuss with Erik/Morris/Robert!
      }

      // add this element to the meta-model
      if (element.getTagName().equals("menu"))
      {
        Menu menu = new Menu(name, parent);
        menu.setLabel(label);
        menu.setGroup(group);
        menu.setGroupRead(groupRead);
        menu.setNamespace(namespace);

        if (group != null && groupRead != null && group.equals(groupRead))
        {
          throw new MolgenisModelException(
              "You cannot assign both read/write and read rights on a single menu");
        }

        if (element.getAttribute("position") == null || !element.getAttribute("position").isEmpty())
        {
          menu.setPosition(Menu.Position.getPosition(element.getAttribute("position")));
        }

        new_parent = menu;
      }
      else if (element.getTagName().equals("form"))
      {
        if (name.isEmpty())
        {
          name = element.getAttribute("entity");
        }
        Form form = new Form(name, parent);
        form.setLabel(label);
        form.setGroup(group);
        form.setGroupRead(groupRead);

        if (group != null && groupRead != null && group.equals(groupRead))
        {
          throw new MolgenisModelException(
              "You cannot assign both read/write and read rights on a single form");
        }

        /** Optional custom header for the selected form screen */
        String header = element.getAttribute("header");
        if (!header.isEmpty()) form.setHeader(header);

        /** Optional description for the selected form screen */
        String description = element.getAttribute("description");
        if (!description.isEmpty()) form.setDescription(description);

        form.setNamespace(namespace);
        new_parent = form;

        // VIEWTYPE
        if (element.getAttribute("view").equals("record"))
        {
          element.setAttribute("view", "edit");
        }
        if (element.getAttribute("view").isEmpty())
        {
          if (element.getChildNodes().getLength() > 0)
          {
            element.setAttribute("view", "edit");
          }
          else
          {
            element.setAttribute("view", "list");
          }
        }
        if (Form.ViewType.parseViewType(element.getAttribute("view")) == Form.ViewType.VIEWTYPE_UNKNOWN)
        {
          throw new MolgenisModelException("view '" + element.getAttribute("view") + "' unknown for form '"
              + form.getName() + "'");
        }
        form.setViewType(Form.ViewType.parseViewType(element.getAttribute("view")));

        // LIMIT
        form.setLimit(10);
        String limit = element.getAttribute("limit");
        if (limit != null && !limit.isEmpty())
        {
          form.setLimit(Integer.parseInt(limit));
        }

        // ACTIONS
        form.setCommands(new ArrayList<String>());
        String commands = element.getAttribute("commands");
        if (commands != null && !commands.isEmpty())
        {
          String[] commandArray = commands.split(",");
          for (String command : commandArray)
          {
            form.getCommands().add(command.trim());
          }
        }

        // SORT
        String sortby = element.getAttribute("sortby");
        if (sortby != null && !sortby.isEmpty())
        {
          // TODO ensure valid sort field
          form.setSortby(sortby);
        }
        String sortorder = element.getAttribute("sortorder");
        if (sortorder != null && !sortorder.isEmpty())
        {
          if (!sortorder.equalsIgnoreCase(Form.SortOrder.ASC.toString())
              && !sortorder.equalsIgnoreCase(Form.SortOrder.DESC.toString()))
          {
            throw new MolgenisModelException(
                "sortorder can only be 'asc' or 'desc'. Parser found <form name=\"" + form.getName()
                    + "\" sortorder=\"" + sortorder + "\"");
          }
          else
          {

            form.setSortorder(SortOrder.parse(sortorder));
          }
        }

        // FILTER
        String filter = element.getAttribute("filter");
        if (filter != null && filter.equals("true"))
        {
          if (element.getAttribute("filterfield") != null && element.getAttribute("filterfield").isEmpty())
          {
            throw new MolgenisModelException("filterfield is missing for subform of screen '"
                + parent.getName() + "'");
          }
          if (element.getAttribute("filtertype") != null && element.getAttribute("filtertype").isEmpty())
          {
            throw new MolgenisModelException("filtertype is missing for subform of screen '"
                + parent.getName() + "'");
          }
          if (element.getAttribute("filtervalue") != null && element.getAttribute("filtervalue").isEmpty())
          {
            logger.warn("filtervalue is missing for subform of screen '" + parent.getName() + "'");
          }
          form.setFilter(true);
          form.setFilterfield(element.getAttribute("filterfield"));
          form.setFiltertype(element.getAttribute("filtertype"));
          form.setFiltervalue(element.getAttribute("filtervalue"));
        }

        // READONLY
        form.setReadOnly(false);
        String readonly = element.getAttribute("readonly");
        if (readonly != null)
        {
          form.setReadOnly(Boolean.parseBoolean(readonly));
        }

        // ENTITY
        // TODO: whould have expected this in the constructor!
        Entity entity = (Entity) model.getDatabase().getChild(element.getAttribute("entity"));
        if (entity == null)
        {
          throw new MolgenisModelException("Could not find the specified entity '"
              + element.getAttribute("entity") + "' for form '" + form.getName() + "'");
        }
        form.setRecord((Record) entity);// form.setEntity(entity);

        // HIDDEN FIELDS
        form.setHideFields(new ArrayList<String>());
        String hide_fields = element.getAttribute("hide_fields");
        if (hide_fields != null && !hide_fields.isEmpty())
        {
          String[] hiddenFieldArray = hide_fields.split(",");
          for (String field : hiddenFieldArray)
          {
            Field f = entity.getAllField(field.trim());
            if (f == null)
            {
              throw new MolgenisModelException("Could not find field '" + field
                  + "' defined in hide_fields='" + element.getAttribute("hide_fields")
                  + "' in form '" + form.getName() + "'");
            }
            // use name from 'f' to correct for case problems
            form.getHideFields().add(f.getName());
          }
        }

        // COMPACT_FIELDS
        if (!element.getAttribute("compact_view").isEmpty())
        {
          String[] fields = element.getAttribute("compact_view").split(",");
          // check if the fields are there
          List<String> compact_fields = new ArrayList<String>();
          for (String field : fields)
          {
            Field f = entity.getAllField(field);
            if (f == null)
            {
              throw new MolgenisModelException("Could not find field '" + field
                  + "' defined in compact_view='" + element.getAttribute("compact_view")
                  + "' in form '" + form.getName() + "'");
            }
            // use name from 'f' to correct for case problems

            compact_fields.add(form.getEntity().getName() + "_" + f.getName());
          }
          form.setCompactView(compact_fields);
        }
      }
      else if (element.getTagName().equals("tree"))
      {
        // check required properties
        if (element.getAttribute("parentfield") != null && element.getAttribute("parentfield").isEmpty())
        {
          throw new MolgenisModelException("parentfield is missing for tree screen '" + name + "'");
        }
        if (element.getAttribute("idfield") != null && element.getAttribute("idfield").isEmpty())
        {
          throw new MolgenisModelException("idfield is missing for tree screen '" + name + "'");
        }
        if (element.getAttribute("labelfield") != null && element.getAttribute("labelfield").isEmpty())
        {
          throw new MolgenisModelException("labelfield is missing for tree screen '" + name + "'");
        }

        Tree tree = new Tree(name, parent, element.getAttribute("parentfield"),
            element.getAttribute("idfield"), element.getAttribute("labelfield"));
        tree.setLabel(label);
        tree.setGroup(group);
        tree.setGroupRead(groupRead);

        if (group != null && groupRead != null && group.equals(groupRead))
        {
          throw new MolgenisModelException(
              "You cannot assign both read/write and read rights on a single tree");
        }

        tree.setNamespace(namespace);
        new_parent = tree;

        // READONLY
        tree.setReadOnly(true);
        String readonly = element.getAttribute("readonly");
        if (readonly != null)
        {
          tree.setReadOnly(Boolean.parseBoolean(readonly));
        }

        // ENTITY
        // TODO: whould have expected this in the constructor!
        DBSchema entity = model.getDatabase().getChild(element.getAttribute("entity"));
        if (entity == null)
        {
          throw new MolgenisModelException("Could not find the specified entity '"
              + element.getAttribute("entity") + "'");
        }
        tree.setRecord((Record) entity);
      }
      else if (element.getTagName().equals("plugin"))
      {
        if (element.getAttribute("type") != null && element.getAttribute("type").isEmpty())
        {
          throw new MolgenisModelException("plugin has no name");
        }
        Plugin plugin = new Plugin(name, parent, element.getAttribute("type"));
        plugin.setLabel(label);
        plugin.setGroup(group);
        plugin.setGroupRead(groupRead);

        if (group != null && groupRead != null && group.equals(groupRead))
        {
          throw new MolgenisModelException(
              "You cannot assign both read/write and read rights on a single plugin");
        }

        plugin.setNamespace(namespace);
        new_parent = plugin;

        // METHOD
        String method = element.getAttribute("flavor");
        if (!"".equals(method))
        {
          plugin.setPluginMethod(Flavor.getPluginMethod(method));
        }

        // READONLY
        plugin.setReadOnly(false);
        String readonly = element.getAttribute("readonly");
        if (readonly != null)
        {
          plugin.setReadOnly(Boolean.parseBoolean(readonly));
        }
      }
      /*
       * else { // this is the unexpected throw new Exception("Encountered
       * unknown element: " + element.getTagName()); }
       */

      // recurse the children
      NodeList children = element.getChildNodes();

      for (int i = 0; i < children.getLength(); i++)
      {
        Node child = children.item(i);

        if (child.getNodeType() != Node.ELEMENT_NODE)
        {
          continue;
        }

        parseUiSchema(model, (Element) child, new_parent);
      }
    }
  }

  private static Document parseXmlFile(String filename) throws MolgenisModelException
  {
    Document document = null;
    DocumentBuilder builder = null;
    try
    {
      // initialize the document
      builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
      document = builder.parse(filename);
    }
    catch (ParserConfigurationException e)
    {
      throw new RuntimeException(e);
    }
    catch (Exception e)
    {
      try
      {
        // try to load from classpath
        document = builder.parse(ClassLoader.getSystemResourceAsStream(filename.trim()));
      }
      catch (Exception e2)
      {
        logger.error("parsing of file '" + filename + "' failed.");
        e.printStackTrace();
        throw new MolgenisModelException("Parsing of DSL (ui) failed: " + e.getMessage());
      }
    }
    return document;
  }

  public static Model parseUiSchema(String filename, Model model) throws MolgenisModelException
  {
    logger.debug("parsing ui file: " + filename);
    if (filename == null || filename.isEmpty())
    {
      return model;
    }

    Document document = parseXmlFile(filename);

    // retrieve the document-root
    Element document_root = document.getDocumentElement();
    if (document_root.getAttribute("name") != null && document_root.getAttribute("name").isEmpty()
        && model.getName().isEmpty())
    {
      document_root.setAttribute("name", "molgenis");
    }
    String modelName = document_root.getAttribute("name");
    model.setName(modelName);

    String label = document_root.getAttribute("label");
    if (!"".equals(label))
    {
      model.setLabel(label);
    }
    // FIXME should be solved by using modules
    // alternatively ui should be in predefined dir anyway...

    // set the package name for the UI
    // model.setName("ui");

    // retrieve the children
    NodeList children = document_root.getChildNodes();

    // Menu main = new Menu("main", model.getUserinterface());
    // main.setLabel("main");
    // main.setNamespace(model.getName());
    for (int i = 0; i < children.getLength(); i++)
    {
      Node child = children.item(i);

      if (child.getNodeType() != Node.ELEMENT_NODE)
      {
        continue;
      }
      try
      {
        // root must be menu
        if (child.getNodeName().equals("description") || child.getNodeName().equals("form")
            || child.getNodeName().equals("plugin") || child.getNodeName().equals("menu")
            || child.getNodeName().equals("include"))
        {
          parseUiSchema(model, (Element) child, model.getUserinterface());
        }
        else
        {
          throw new MolgenisModelException("Unrecognized element: " + child.getNodeName());
        }

        // }
      }
      catch (Exception e)
      {
        e.printStackTrace();
        throw new MolgenisModelException(e.getMessage());
      }

    }
    return model;
  }

  private static String elementValueToString(Element element)
  {
    StringWriter writer = new StringWriter();

    OutputFormat format = new OutputFormat(element.getOwnerDocument());
    format.setIndenting(true);
    format.setOmitXMLDeclaration(true);

    XMLSerializer serializer = new XMLSerializer(writer, format);
    try
    {
      serializer.serialize(element);
    }
    catch (IOException e)
    {
      e.printStackTrace();
    }

    String xml = writer.toString();
    // FIXME: attributes
    xml = xml.replace("<" + element.getTagName() + ">", "");
    xml = xml.replace("</" + element.getTagName() + ">", "");
    xml = xml.replace("<" + element.getTagName() + "/>", "");

    return xml;
  }

  private static final Logger logger = Logger.getLogger(MolgenisModelParser.class.getName());
}
TOP

Related Classes of org.molgenis.model.MolgenisModelParser

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.