View Javadoc
1   /*
2    * This file is part of ***  M y C o R e  ***
3    * See http://www.mycore.de/ for details.
4    *
5    * MyCoRe is free software: you can redistribute it and/or modify
6    * it under the terms of the GNU General Public License as published by
7    * the Free Software Foundation, either version 3 of the License, or
8    * (at your option) any later version.
9    *
10   * MyCoRe is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU General Public License for more details.
14   *
15   * You should have received a copy of the GNU General Public License
16   * along with MyCoRe.  If not, see <http://www.gnu.org/licenses/>.
17   */
18  
19  package org.mycore.frontend.xeditor;
20  
21  import java.util.ArrayList;
22  import java.util.HashMap;
23  import java.util.Iterator;
24  import java.util.List;
25  import java.util.Map;
26  
27  import org.jdom2.Attribute;
28  import org.jdom2.Document;
29  import org.jdom2.Element;
30  import org.jdom2.filter.Filters;
31  import org.jdom2.xpath.XPathExpression;
32  import org.jdom2.xpath.XPathFactory;
33  import org.mycore.common.MCRConstants;
34  
35  public class MCRXMLCleaner {
36  
37      private static final MCRCleaningRuleningRule">MCRCleaningRule REMOVE_EMPTY_ATTRIBUTES = new MCRCleaningRule("//@*", "string-length(.) > 0");
38  
39      private static final MCRCleaningRuleeaningRule">MCRCleaningRule REMOVE_EMPTY_ELEMENTS = new MCRCleaningRule("//*",
40          "@* or * or (string-length(text()) > 0)");
41  
42      private static final MCRCleaningRulee">MCRCleaningRule PRESERVE_STRUCTURE_AND_SERVICE = new MCRCleaningRule(
43          "/mycoreobject/structure|/mycoreobject/service", "true()");
44  
45      private List<MCRCleaningRule> rules = new ArrayList<>();
46  
47      private Map<Object, MCRCleaningRule> nodes2rules = new HashMap<>();
48  
49      public MCRXMLCleaner() {
50          addRule(REMOVE_EMPTY_ATTRIBUTES);
51          addRule(REMOVE_EMPTY_ELEMENTS);
52          addRule(PRESERVE_STRUCTURE_AND_SERVICE);
53      }
54  
55      public void addRule(String xPathExprNodesToInspect, String xPathExprRelevancyTest) {
56          addRule(new MCRCleaningRule(xPathExprNodesToInspect, xPathExprRelevancyTest));
57      }
58  
59      public void addRule(MCRCleaningRule rule) {
60          rules.remove(rule);
61          rules.add(rule);
62      }
63  
64      public Document clean(Document xml) {
65          Document clone = xml.clone();
66          do
67              mapNodesToRules(clone);
68          while (clean(clone.getRootElement()));
69          return clone;
70      }
71  
72      private void mapNodesToRules(Document xml) {
73          nodes2rules.clear();
74          for (MCRCleaningRule rule : rules)
75              for (Object object : rule.getNodesToInspect(xml))
76                  nodes2rules.put(object, rule);
77      }
78  
79      private boolean clean(Element element) {
80          boolean changed = false;
81  
82          for (Iterator<Element> children = element.getChildren().iterator(); children.hasNext();) {
83              Element child = children.next();
84              if (clean(child))
85                  changed = true;
86              if (!isRelevant(child)) {
87                  changed = true;
88                  children.remove();
89              }
90          }
91  
92          for (Iterator<Attribute> attributes = element.getAttributes().iterator(); attributes.hasNext();) {
93              Attribute attribute = attributes.next();
94              if (!isRelevant(attribute)) {
95                  changed = true;
96                  attributes.remove();
97              }
98          }
99  
100         return changed;
101     }
102 
103     private boolean isRelevant(Object node) {
104         MCRCleaningRule rule = nodes2rules.get(node);
105         return (rule == null || rule.isRelevant(node));
106     }
107 }
108 
109 class MCRCleaningRule {
110 
111     private String xPathExprNodesToInspect;
112 
113     private XPathExpression<Object> xPathNodesToInspect;
114 
115     private XPathExpression<Object> xPathRelevancyTest;
116 
117     public MCRCleaningRule(String xPathExprNodesToInspect, String xPathExprRelevancyTest) {
118         this.xPathExprNodesToInspect = xPathExprNodesToInspect;
119         this.xPathNodesToInspect = XPathFactory.instance().compile(xPathExprNodesToInspect, Filters.fpassthrough(),
120             null,
121             MCRConstants.getStandardNamespaces());
122         this.xPathRelevancyTest = XPathFactory.instance().compile(xPathExprRelevancyTest, Filters.fpassthrough(), null,
123             MCRConstants.getStandardNamespaces());
124     }
125 
126     public List<Object> getNodesToInspect(Document xml) {
127         return xPathNodesToInspect.evaluate(xml);
128     }
129 
130     public boolean isRelevant(Object node) {
131         Object found = xPathRelevancyTest.evaluateFirst(node);
132         if (found == null)
133             return false;
134         else if (found instanceof Boolean)
135             return (Boolean) found;
136         else
137             return true; // something matching found
138     }
139 
140     @Override
141     public boolean equals(Object obj) {
142         if (obj instanceof MCRCleaningRule)
143             return xPathExprNodesToInspect.equals(((MCRCleaningRule) obj).xPathExprNodesToInspect);
144         else
145             return false;
146     }
147 
148     @Override
149     public int hashCode() {
150         return xPathExprNodesToInspect.hashCode();
151     }
152 }