aboutsummaryrefslogtreecommitdiffstats
path: root/config-model/src/main/java/com/yahoo/config/model/builder/xml/XmlHelper.java
blob: 93f308a2c2e8439895c16a11da93645d41262c83 (plain) (blame)
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.config.model.builder.xml;

import com.yahoo.component.ComponentId;
import com.yahoo.component.ComponentSpecification;
import java.util.logging.Level;
import com.yahoo.text.XML;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.logging.Logger;


/**
 * Static methods for helping dom building
 *
 * @author bratseth
 */
public final class XmlHelper {
    private static final Logger log = Logger.getLogger(XmlHelper.class.getPackage().toString());


    private static final String idReference = "idref";
    // Access to this needs to be synchronized (as it is in getDocumentBuilder() below)
    public static final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

    static {
        XmlHelper.factory.setNamespaceAware(true);
        // if use jdom and jaxen this will fail badly:
        XmlHelper.factory.setXIncludeAware(true);
    }

    private XmlHelper() {}

    public static String nullIfEmpty(String attribute) {
        if (attribute.isEmpty())
            return null;
        else
            return attribute;
    }

    /**
     * For searchers inside search chains, the id may be both a reference and an id at once, or just a reference.
     * In other cases, it is clear which one it is from context, so I think the difference is not worth bothering users
     * with, unless they are XML purists in which case they will have the option of writing this correctly.
     * - Jon
     */
    public static String getIdString(Element element) {
        String idString = element.getAttribute("id");
        if (idString == null || idString.trim().equals(""))
            idString = element.getAttribute(idReference);
        if (idString == null || idString.trim().equals(""))
            idString = element.getAttribute("ident");
        return idString;
    }

    public static ComponentId getId(Element element) {
        return new ComponentId(getIdString(element));
    }

    public static ComponentSpecification getIdRef(Element element) {
        return new ComponentSpecification(getIdString(element));
    }

    public static Document getDocument(Reader reader) {
        Document doc;
        try {
            doc = getDocumentBuilder().parse(new InputSource(reader));
        } catch (SAXException | IOException e) {
            throw new IllegalArgumentException(e);
        }
        return doc;
    }

    public static List<String> splitAndDiscardEmpty(String field, String regex) {
        List<String> ret = new ArrayList<>();
        for (String t : field.split(regex)) {
            if (!t.isEmpty()) {
                ret.add(t);
            }
        }
        return ret;
    }

    public static List<String> spaceSeparatedSymbols(String field) {
        return splitAndDiscardEmpty(field, " ");
    }

    public static Collection<String> spaceSeparatedSymbolsFromAttribute(Element spec, String name) {
        return spaceSeparatedSymbols(spec.getAttribute(name));
    }

    public static Collection<String> valuesFromElements(Element parent, String elementName) {
        List<String> symbols = new ArrayList<>();
        for (Element symbol : XML.getChildren(parent, elementName)) {
            symbols.add(XML.getValue(symbol).trim());
        }
        return symbols;
    }

    public static boolean isReference(Element element) {
        return element.hasAttribute(idReference);
    }

    /**
     * Creates a new XML document builder.
     *
     * @return A new DocumentBuilder instance, or null if we fail to get one.
     */
    public static synchronized DocumentBuilder getDocumentBuilder() {
        try {
            DocumentBuilder docBuilder = factory.newDocumentBuilder();
            log.log(Level.FINE, "XML parser now operational!");
            return docBuilder;
        } catch (ParserConfigurationException e) {
            log.log(LogLevel.WARNING, "No XML parser available - " + e);
            return null;
        }
    }

    public static Optional<String> getOptionalAttribute(Element element, String name) {
        return Optional.ofNullable(element.getAttribute(name)).filter(s -> !s.isEmpty());
    }

    public static Optional<Element> getOptionalChild(Element parent, String childName) {
        return Optional.ofNullable(XML.getChild(parent, childName));

    }

    public static Optional<String> getOptionalChildValue(Element parent, String childName) {
        Element child = XML.getChild(parent, childName);
        if (child == null) return Optional.empty();
        if (child.getFirstChild() == null) return Optional.empty();
        return Optional.ofNullable(child.getFirstChild().getNodeValue());
    }
}