File: MjXmlModifiers.cs

package info (click to toggle)
mujoco 2.2.2-3.2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 39,796 kB
  • sloc: ansic: 28,947; cpp: 28,897; cs: 14,241; python: 10,465; xml: 5,104; sh: 93; makefile: 34
file content (115 lines) | stat: -rw-r--r-- 4,859 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
// Copyright 2019 DeepMind Technologies Limited
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using UnityEngine;

namespace Mujoco {

  public class MjXmlModifiers {
    private XmlDocument _root;

    public MjXmlModifiers(XmlDocument root) {
      _root = root;
    }

    public void ApplyModifiersToElement(XmlElement element) {
      // Combine all defaults into one. At this stage, we want to overwrite attributes defined by
      // the previous defaults.
      var aggregateDefaults = _root.CreateElement("aggregate");
      // Root default leaf should be processed only once, and handled first (so it's overriden).
      var rootDefaultLeaf = _root.SelectSingleNode($"/mujoco/default/{element.Name}") as XmlElement;
      if (rootDefaultLeaf != null) {
        CopyAttributes(rootDefaultLeaf, aggregateDefaults);
      }
      // Order matters - reverse class list so that most relevant is last.
      var classes = GetApplicableDefaultClasses(element).Reverse();
      foreach (var className in classes) {
        var defaultClassElement =
            _root.SelectSingleNode($"descendant::default[@class='{className}']") as XmlElement;
        // Ancestry iterates up in the tree, but we want to apply changes from remote to specific.
        var ancestors = GetDefaultAncestry(defaultClassElement, element.Name).Reverse();
        foreach (var defaultAncestor in ancestors) {
          CopyAttributesOverwriteExisting(defaultAncestor, aggregateDefaults);
        }
      }
      // Add the merged attribiutes to the node, without overwriting the existing values.
      CopyAttributes(aggregateDefaults, element);
    }

    private IEnumerable<XmlElement> GetDefaultAncestry(XmlElement classElement, string nodeType) {
      var defaultElement = classElement;
      var top = _root.SelectSingleNode("/mujoco/default");
      while (defaultElement != top) {
        foreach (var element in defaultElement.ChildNodes) {
          if (((XmlElement)element).Name == nodeType) {
            yield return (XmlElement)element;
          }
        }
        defaultElement = defaultElement.ParentNode as XmlElement;
      }
    }

    // Return a list of names of all the applicable classes.  Implemented by climbing up the tree,
    // hence results are in order of relevance, from the most specific to most remotely-inherited.
    public static IEnumerable<string> GetApplicableDefaultClasses(XmlElement modifiedElement) {
      var nodeType = modifiedElement.Name;

      // Build the inheritance list for the element that's meant to be modified.
      // It will consist of all qualifiers supplied by the element itself and its parents
      // using "class" and "childclass" values.
      var inheritance = new List<string>();
      var element = modifiedElement;
      while (element != null) {
        var className = element.GetStringAttribute("childclass", string.Empty);
        if (element == modifiedElement) {
          // "class" attribute does not partcipate in building of the inheritance tree, and so we're
          // only considering it if it's defined on the very element we're trying to modify.
          className = element.GetStringAttribute("class", className);
        }
        if (!string.IsNullOrEmpty(className) && !inheritance.Contains(className)) {
          inheritance.Add(className);
        }
        element = element.ParentNode as XmlElement;
      }
      return inheritance;
    }

    private static string GetElementClass(XmlElement element) {
      var elementClass = element.GetStringAttribute("class", string.Empty);
      if (string.IsNullOrEmpty(elementClass)) {
        elementClass = element.GetStringAttribute("childclass", string.Empty);
      }
      return elementClass;
    }

    public static void CopyAttributes(XmlElement from, XmlElement to) {
      foreach (XmlAttribute attribute in from.Attributes) {
        if (to.HasAttribute(attribute.Name) == false) {
          to.SetAttribute(attribute.Name, attribute.Value);
        }
      }
    }

    public static void CopyAttributesOverwriteExisting(XmlElement from, XmlElement to) {
      foreach (XmlAttribute attribute in from.Attributes) {
        to.SetAttribute(attribute.Name, attribute.Value);
      }
    }
  }
}