All Downloads are FREE. Search and download functionalities are using the official Maven repository.

host.anzo.commons.io.xml.XmlParser Maven / Gradle / Ivy

There is a newer version: 1.29
Show newest version
package host.anzo.commons.io.xml;

import host.anzo.commons.utils.ZipUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author ANZO
 */
public class XmlParser implements AutoCloseable {
	private final Path filePath;
	private final String name;
	private final String content;
	private final Map nameAttributes = new HashMap<>();
	private final Map> nameChildren = new HashMap<>();

	@SuppressWarnings("unused")
	private InputStream inputStream;

	private static final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
	static {
		documentBuilderFactory.setIgnoringComments(true);
		documentBuilderFactory.setIgnoringElementContentWhitespace(true);
	}

	/**
	 * @param fileName file name
	 * @return XmlParser loaded from specified file
	 */
	public static @NotNull XmlParser fromFile(@NotNull String fileName) {
		try (FileInputStream stream = new FileInputStream(fileName)) {
			return new XmlParser(Paths.get(fileName), Objects.requireNonNull(rootElement(stream)));
		}
		catch (IOException e) {
			throw new RuntimeException("Can't parse XML file:" + fileName, e);
		}
	}

	/**
	 * @param path the file path
	 * @return XmlParser loaded from the specified file path
	 */
	public static @NotNull XmlParser fromPath(@NotNull Path path) {
		try (FileInputStream stream = new FileInputStream(path.toFile())) {
			return new XmlParser(path, Objects.requireNonNull(rootElement(stream)));
		}
		catch (IOException e) {
			throw new RuntimeException("Can't parse XML file:" + path, e);
		}
	}

	/**
	 * @param fileNames zip file names
	 * @return list of XmlParsers loaded from specified zip files
	 */
	public static @NotNull List fromZip(String @NotNull ... fileNames) {
		final List results = new ArrayList<>();
		for (String fileName : fileNames) {
			try(FileSystem zipFileSystem = ZipUtils.createZipFileSystem(fileName, false)) {
				if (zipFileSystem != null) {
					try(final Stream filePathsInZip = Files.walk(zipFileSystem.getPath("/"))) {
						filePathsInZip.forEach(filePathInZip -> {
							if (filePathInZip.toString().endsWith(".xml")) {
								try (InputStream stream = Files.newInputStream(filePathInZip)) {
									results.add(XmlParser.fromInputStream(filePathInZip, stream));
								}
								catch (IOException e) {
									throw new RuntimeException("Can't parse XML file name=" + filePathInZip + " from zip archive name=" + fileName, e);
								}
							}
						});
					}
				}
			}
			catch (IOException e) {
				throw new RuntimeException("Can't load XML files from zip archive name=" + fileName, e);
			}
		}
		return results;
	}

	/**
	 * @param fileName resource file name
	 * @param classLoader class loader
	 * @return XmlParser loaded from specified resource
	 * @throws IOException when resource can't be read
	 */
	public static @NotNull XmlParser fromResource(String fileName, @NotNull ClassLoader classLoader) throws IOException {
		return fromInputStream(Paths.get(fileName), classLoader.getResourceAsStream(fileName));
	}

	/**
	 * @param filePath file path
	 * @param inputStream input stream to read
	 * @return XmlParser loaded from specified input stream
	 * @throws IOException when input stream can't be read
	 */
	public static @NotNull XmlParser fromInputStream(Path filePath, InputStream inputStream) throws IOException {
		return new XmlParser(filePath, Objects.requireNonNull(rootElement(inputStream)));
	}

	private XmlParser(Path filePath, @NotNull Element element) {
		this.filePath = filePath;
		this.name = element.getNodeName();
		this.content = element.getTextContent();

		final NamedNodeMap namedNodeMap = element.getAttributes();
		int nodeCount = namedNodeMap.getLength();
		for (int i = 0; i < nodeCount; i++) {
			final Node node = namedNodeMap.item(i);
			final String name = node.getNodeName();
			this.nameAttributes.put(name.toLowerCase(), node.getNodeValue());
		}

		final NodeList nodes = element.getChildNodes();
		nodeCount = nodes.getLength();
		for (int i = 0; i < nodeCount; i++) {
			final Node node = nodes.item(i);
			final int type = node.getNodeType();
			if (type == Node.ELEMENT_NODE) {
				final XmlParser child = new XmlParser(filePath, (Element) node);
				this.nameChildren.computeIfAbsent(node.getNodeName().toLowerCase(), k -> new ArrayList<>()).add(child);
			}
		}
	}

	public String getFileName() {
		if (filePath == null) {
			return "";
		}
		return filePath.getFileName().toString();
	}

	public String getDirectoryName() {
		if (filePath == null) {
			return "";
		}
		return filePath.getParent().getFileName().toString();
	}

	public String name() {
		return name;
	}

	public String content() {
		return content;
	}

	public XmlParser child(String name) {
		final XmlParser child = optChild(name);
		if (child == null) {
			throw new RuntimeException("Could not find child node: " + name);
		}
		return child;
	}

	public XmlParser optChild(String name) {
		final List children = children(name);
		int n = children.size();
		if (n > 1) {
			throw new RuntimeException("Could not find individual child node: " + name);
		}
		return n == 0 ? null : children.stream().findFirst().orElse(null);
	}

	public List children() {
		return nameChildren.values().stream().flatMap(Collection::stream).collect(Collectors.toList());
	}

	public List children(@NotNull String name) {
		final List children = nameChildren.get(name.toLowerCase());
		return children == null ? new ArrayList<>() : children;
	}

	public Set attributeNames() {
		return nameAttributes.keySet();
	}

	public boolean hasAttribute(@NotNull String attributeName) {
		return nameAttributes.containsKey(attributeName.toLowerCase());
	}

	public String readString(@NotNull String name, String defaultValue) {
		final String value = nameAttributes.get(name.toLowerCase());
		return value != null ? value : defaultValue;
	}

	public String readString(String name) {
		return readString(name, null);
	}

	public int readInt(String name, int defaultValue) {
		final String result = Optional.ofNullable(readString(name)).orElse("").replace("_","");
		if (NumberUtils.isCreatable(result)) {
			return Integer.parseInt(result);
		}
		return defaultValue;
	}

	public int readInt(String name) {
		return Integer.parseInt(Optional.ofNullable(readString(name)).orElse("").replace("_",""));
	}

	public int readIntHex(String name) {
		return Integer.decode(readString(name));
	}

	public long readLong(String name, long defaultValue) {
		final String result = Optional.ofNullable(readString(name)).orElse("").replace("_","");
		if (NumberUtils.isCreatable(result)) {
			return Long.parseUnsignedLong(result);
		}
		return defaultValue;
	}

	public long readLong(String name) {
		return Long.parseLong(Optional.ofNullable(readString(name)).orElse("").replace("_",""));
	}

	public float readFloat(String name, float defaultValue) {
		final String result = Optional.ofNullable(readString(name)).orElse("").replace("_","");
		if (!StringUtils.isEmpty(result)) {
			return Float.parseFloat(result);
		}
		return defaultValue;
	}

	public float readFloat(String name) {
		return Float.parseFloat(Optional.ofNullable(readString(name)).orElse("").replace("_",""));
	}

	public boolean readBoolean(String name, boolean defaultValue) {
		final String result = readString(name);
		if (result != null) {
			return Boolean.parseBoolean(result.toLowerCase());
		}
		return defaultValue;
	}

	public boolean readBoolean(String name) {
		return Boolean.parseBoolean(readString(name).toLowerCase());
	}

	public final > T readEnum(String name, Class enumClass, T defaultValue) {
		final String value = readString(name);
		if (value == null || StringUtils.isEmpty(value.trim())) {
			return defaultValue;
		}
		try {
			if (StringUtils.isNumeric(value)) {
				return enumClass.getEnumConstants()[Integer.parseInt(value)];
			}
			else {
				return Enum.valueOf(enumClass, value);
			}
		}
		catch (Exception e) {
			throw new IllegalArgumentException("Enum value of type " + enumClass.getName() + " required, but found: " + value, e);
		}
	}

	public List readStringList(String name, String splitter) {
		final String value = readString(name);
		if (StringUtils.isEmpty(value)) {
			return Collections.emptyList();
		}
		return Arrays.stream(value.split(splitter)).map(String::trim).collect(Collectors.toList());
	}

	private static @Nullable Element rootElement(InputStream inputStream) throws IOException {
		try {
			final DocumentBuilder builder = documentBuilderFactory.newDocumentBuilder();
			if (builder != null) {
				return builder.parse(inputStream).getDocumentElement();
			}
			return null;
		}
		catch (IOException | SAXException | ParserConfigurationException exception) {
			throw new RuntimeException("Can't parse XML document", exception);
		}
		finally {
			inputStream.close();
		}
	}

	@Override
	public void close() throws Exception {
		if (inputStream != null) {
			inputStream.close();
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy