Package sk.fiit.jim.init

Source Code of sk.fiit.jim.init.SkillsFromXmlLoader

package sk.fiit.jim.init;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import static sk.fiit.jim.log.LogType.INIT;

import sk.fiit.jim.agent.moves.*;
import sk.fiit.jim.log.Log;
import sk.fiit.jim.log.LogType;
import sk.fiit.jim.math.MathExpressionEvaluator;

/**
*  SkillsFromXmlLoader.java
*
*    Loads list of moves from ALL xml files contained in supplied directory. Populates
*    {@link LowSkills} and {@link Phases} cache with parsed data
*
*
*@Title        Jim
*@author       $Author: marosurbanec $
*/
public class SkillsFromXmlLoader{
 
  File currentlyProcessed;
  File directoryWithXmls;
  Phase currentPhase;
  Set<String> requiredPhases = new HashSet<String>();
  Map<String, Double> constants = new HashMap<String, Double>();
 
  public SkillsFromXmlLoader(File directoryWithXmls){
    this.directoryWithXmls = directoryWithXmls;
  }

  public void load()  throws ParserConfigurationException, SAXException, IOException, XPathExpressionException{
    if (!directoryWithXmls.isDirectory())
      throw new IllegalArgumentException(directoryWithXmls.getAbsolutePath()+" is NOT a directory");
    //ignore non-xml files
    File[] xmlFiles = directoryWithXmls.listFiles(new FilenameFilter(){
      public boolean accept(File dir, String name){
        return name.endsWith(".xml") && !name.startsWith(".");
      }
    });
   
    for (File xml : xmlFiles)
      loadMovesFrom(xml);

    for (String phase : requiredPhases)
      if (!Phases.exists(phase))
        Log.error(LogType.INIT, "Phase %s is referenced, yet undeclared", phase);
  }

  private void loadMovesFrom(File xml) throws ParserConfigurationException, SAXException, IOException, XPathExpressionException{
    Log.log(LogType.INIT, "Loading moves from %s", xml.getAbsolutePath());
    currentlyProcessed = xml;
    //in order to avoid creating whitespace elements due to \n\t characters
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    dbf.setIgnoringElementContentWhitespace(true);
    Document document = dbf.newDocumentBuilder().parse(xml);
   
    XPath xpath = XPathFactory.newInstance().newXPath();
    //load constants
    NodeList constants = (NodeList) xpath.compile("/robot/constants/constant").evaluate(document, XPathConstants.NODESET);
    for (Node constant = constants.item(0); constant != null ; constant = constant.getNextSibling())
      appendToConstants(constant);
   
    //load low skills
    NodeList lowSkills = (NodeList) xpath.compile("/robot/low_skills/low_skill").evaluate(document, XPathConstants.NODESET);
    for (Node lowSkill = lowSkills.item(0); lowSkill != null ; lowSkill = lowSkill.getNextSibling())
      LowSkills.addSkill(createSkill(lowSkill));
   
    //load phases
    NodeList phases = (NodeList) xpath.compile("/robot/phases/phase").evaluate(document, XPathConstants.NODESET);
    for (Node phase = phases.item(0); phase != null ; phase = phase.getNextSibling())
      Phases.addPhase(createPhase(phase));
  }
 
  private String getNodeAttribute(Node phaseNode, String attribute){
    return phaseNode.getAttributes().getNamedItem(attribute).getNodeValue();
  }

  private boolean hasAttribute(Node phaseNode, String attribute){
    return phaseNode.getAttributes().getNamedItem(attribute) != null;
  }

  private void appendToConstants(Node constant){
    String tagName = constant.getNodeName();
    if (!"constant".equals(tagName))
      return;
    String name = getNodeAttribute(constant, "name");
    String value = getNodeAttribute(constant, "value");
    constants.put(name, Double.valueOf(value));
  }

  private LowSkill createSkill(Node lowSkillNode){
    String tagName = lowSkillNode.getNodeName();
    if (!"low_skill".equals(tagName))
      return null;
    if (!hasAttribute(lowSkillNode, "name"))
      throw new IllegalStateException("Low skill without name encountered in "+currentlyProcessed.getAbsolutePath());
   
    if (!hasAttribute(lowSkillNode, "firstPhase"))
      throw new IllegalStateException("Low skill without firstPhase encountered in "+currentlyProcessed.getAbsolutePath());
   
    String skillName = getNodeAttribute(lowSkillNode, "name");
    Log.debug(LogType.INIT, "Loading low skill %s", skillName);
    String initialPhase = getNodeAttribute(lowSkillNode, "firstPhase");
    LowSkill lowSkill = new LowSkill();
    lowSkill.initialPhase = initialPhase;
    lowSkill.name = skillName;
    Log.debug(LogType.INIT, String.format("Low skill %s: initialPhase: %s", lowSkill.name, lowSkill.initialPhase));
    requiredPhases.add(initialPhase);
    return lowSkill;
  }
 
  private Phase createPhase(Node phaseNode){
    if (!"phase".equals(phaseNode.getNodeName()))
      return null;
   
    Phase phase = new Phase();
    currentPhase = phase;
    phase.effectors = new ArrayList<EffectorData>();
    phase.name = phaseNode.getAttributes().getNamedItem("name").getNodeValue();
    Log.debug(LogType.INIT, "Loading phase %s", phase.name);
    populatePhaseAttributes(phaseNode, phase);
   
    for (int i = 0 ; i < phaseNode.getChildNodes().getLength() ; i++){
      Node child = phaseNode.getChildNodes().item(i);
     
      if ("effectors".equalsIgnoreCase(child.getNodeName()))
        appendEffectorsToPhase(child);
      if ("duration".equals(child.getNodeName()))
        phase.duration = roundToNearestTwenty(calculateNumericValue(child.getFirstChild().getTextContent()));
     
      if ("finalize".equalsIgnoreCase(child.getNodeName())){
        phase.finalizationPhase = child.getTextContent().trim();
        requiredPhases.add(child.getTextContent().trim());
      }
    }
    validate(phase);
   
    return phase;
  }

  private void populatePhaseAttributes(Node phaseNode, Phase phase){
    if (hasAttribute(phaseNode, "isFinal"))
      phase.isFinal = true;
    if (hasAttribute(phaseNode, "skipIfFlag"))
      phase.skipIfFlag = new SkipFlag(getNodeAttribute(phaseNode, "skipIfFlag"));
    if (hasAttribute(phaseNode, "setFlagTrue"))
      phase.setFlagTrue = new SkipFlag(getNodeAttribute(phaseNode, "setFlagTrue"));
    if (hasAttribute(phaseNode, "setFlagFalse"))
      phase.setFlagFalse = new SkipFlag(getNodeAttribute(phaseNode, "setFlagFalse"));
    if (hasAttribute(phaseNode, "next")){
      phase.next = getNodeAttribute(phaseNode, "next");
      requiredPhases.add(phase.next);
    }
  }

  private void validate(Phase phase){
    if (phase.name == null || phase.name.isEmpty())
      throw new IllegalArgumentException("Phase declared in "+currentlyProcessed.getAbsolutePath()+" has empty or no name");
   
    if (phase.duration == 0.0)
      throw new IllegalArgumentException("Phase "+phase.name+" in "+currentlyProcessed.getAbsolutePath()+" has no duration");
   
    if (phase.next == null && !phase.isFinal)
      throw new IllegalArgumentException("Phase "+phase.name+" in "+currentlyProcessed.getAbsolutePath()+" has no follower");
   
    if (phase.finalizationPhase == null && phase.isFinal)
      throw new IllegalArgumentException("Phase "+phase.name+" in "+currentlyProcessed.getAbsolutePath()+" has no finalization phase");
  }

  /**
   * Agent receives and sends data in a 20ms discrete tick. Therefore, we have to have
   * a timespan rounded to the nearest multiple of 20.
   */
  private double roundToNearestTwenty(Double supplied)
  {
    Double calculated = Double.valueOf(supplied);
    if (supplied.intValue() % 20 >= 10 || supplied < 20)
      calculated = supplied - (supplied.intValue() % 20) + 20.0;
    else
      calculated = supplied - (supplied.intValue() % 20);
   
    if (supplied.intValue() != Double.valueOf(supplied).intValue())
      Log.log(LogType.INIT, "Phase(%s) duration truncated to nearest multiple of 20ms: %s => %.2f", currentPhase.name, supplied, calculated);
   
    return calculated / 1000.0;
  }

  /**
   * Populates phases' effector list
   */
  private void appendEffectorsToPhase(Node effectorsNode){
    for (int i = 0 ; i < effectorsNode.getChildNodes().getLength() ; i++)
    {
      Node effectorNode = effectorsNode.getChildNodes().item(i);
      //whitespace node, ignore it
      if ("#text".equals(effectorNode.getNodeName()) || "#comment".equals(effectorNode.getNodeName())) continue;
     
      if (effectorNode.getAttributes().getNamedItem("end") == null)
        throw new IllegalStateException("Effector without an end position encountered in phase "+currentPhase.name);
      Joint joint = Joint.valueOf(effectorNode.getNodeName().toUpperCase());
      EffectorData tag = new EffectorData();
      tag.effector = joint;
      String endAngle = getNodeAttribute(effectorNode, "end");
      Double endPosition = calculateNumericValue(endAngle);
      Double trimmed = joint.trim(endPosition);
      if (endPosition.intValue() != trimmed.intValue()){
        Log.log(INIT, "Joint %s trimmed from %.2f to %.2f", joint.toString(), endPosition, trimmed);
      }
       
      tag.endAngle = trimmed;
      currentPhase.effectors.add(tag);
    }
  }

  private Double calculateNumericValue(String endAngle){
    for (String constant : constants.keySet())
      endAngle = endAngle.replace(constant, constants.get(constant).toString());
    return new MathExpressionEvaluator(endAngle).getDouble();
  }
}
TOP

Related Classes of sk.fiit.jim.init.SkillsFromXmlLoader

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.