ConfigReader.java
/*
* Copyright (c) 2020 bahlef.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
* Contributors:
* bahlef - initial API and implementation and/or initial documentation
*/
package de.funfried.netbeans.plugins.external.formatter.eclipse.xml;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.netbeans.api.annotations.common.NonNull;
import org.openide.xml.XMLUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import de.funfried.netbeans.plugins.external.formatter.exceptions.ConfigReadException;
import de.funfried.netbeans.plugins.external.formatter.exceptions.ProfileNotFoundException;
/**
* This class reads a config file for Eclipse code formatter.
*
* @author bahlef
*/
public class ConfigReader {
private static final Logger log = Logger.getLogger(ConfigReader.class.getName());
public static final String ATTRIBUTE_PROFILE_KIND = "kind";
public static final String ATTRIBUTE_PROFILE_NAME = "name";
public static final String ATTRIBUTE_SETTING_ID = "id";
public static final String ATTRIBUTE_SETTING_VALUE = "value";
public static final String TAG_NAME_PROFILES = "profiles";
public static final String TAG_NAME_PROFILE = "profile";
public static final String TAG_NAME_SETTING = "setting";
public static final String PROFILE_KIND = "CodeFormatterProfile";
/**
* Reads the content of the given file path and returns it as a {@link String}.
*
* @param filePath a file path
*
* @return the content of the file at the fiven {@code filePath}
*
* @throws IOException if there is an issue accessing the file at the given path
*/
@NonNull
public static String readContentFromFilePath(String filePath) throws IOException {
try {
URL url = new URL(filePath);
return IOUtils.toString(url.openStream(), StandardCharsets.UTF_8);
} catch (IOException ex) {
log.log(Level.FINEST, "Could not read file via URL, fallback to local file reading", ex);
return FileUtils.readFileToString(new File(filePath), StandardCharsets.UTF_8);
}
}
/**
* Parses and returns the key/value pairs from the given {@code fileContent} for the given {@code profileName} as a {@link Map}.
*
* @param fileContent the file content to parse
* @param profileName the profile name for which to get the settings
*
* @return a {@link Map} within all the configuration paramters of the given {@code profileName} read from the given {@code fileContent},
* or throws an exception if there's a problem reading the input, e.g.: invalid XML.
*
* @throws SAXException if there are parsing issues
* @throws IOException if there is an I/O issue
* @throws ConfigReadException if the given {@code fileContent} is not a valid Eclipse formatter template
* @throws ProfileNotFoundException if no profile could be found with the given {@code profileName} in the given {@code fileContent}
*/
@NonNull
public static Map<String, String> getProfileSettings(String fileContent, String profileName) throws ConfigReadException, ProfileNotFoundException, IOException, SAXException {
List<Node> profileNodes = getProfileNodes(fileContent);
for (Node profileTag : profileNodes) {
Node profileNameAttr = profileTag.getAttributes().getNamedItem(ATTRIBUTE_PROFILE_NAME);
if (Objects.equals(profileName, profileNameAttr.getNodeValue())) {
NodeList settingTagList = profileTag.getChildNodes();
Map<String, String> config = new HashMap<>();
for (int s = 0; s < settingTagList.getLength(); s++) {
Node settingTag = settingTagList.item(s);
if (TAG_NAME_SETTING.equals(settingTag.getNodeName())) {
NamedNodeMap attributes = settingTag.getAttributes();
Node keyAttr = attributes.getNamedItem(ATTRIBUTE_SETTING_ID);
if (keyAttr != null) {
Node valueAttr = attributes.getNamedItem(ATTRIBUTE_SETTING_VALUE);
if (valueAttr != null) {
config.put(keyAttr.getNodeValue(), valueAttr.getNodeValue());
}
}
}
}
return config;
}
}
throw new ProfileNotFoundException("Profile " + profileName + " not found in given file content");
}
/**
* Parses the given {@code fileContent} and returns a {@link List} within all profile names found in that {@code fileContent}.
*
* @param fileContent the file content to parse
*
* @return a {@link List} within all profile names found in the given {@code fileContent}
*
* @throws SAXException if there are parsing issues
* @throws IOException if there is an I/O issue
* @throws ConfigReadException if the given {@code fileContent} is not a valid Eclipse formatter template
*/
@NonNull
public static List<String> getProfileNames(String fileContent) throws ConfigReadException, IOException, SAXException {
List<String> profileNames = new ArrayList<>();
List<Node> profileNodes = getProfileNodes(fileContent);
for (Node profileTag : profileNodes) {
Node profileNameAttr = profileTag.getAttributes().getNamedItem(ATTRIBUTE_PROFILE_NAME);
if (profileNameAttr != null) {
String profileName = profileNameAttr.getNodeValue();
if (StringUtils.isNotBlank(profileName)) {
profileNames.add(profileNameAttr.getNodeValue());
}
}
}
return profileNames;
}
/**
* Parses the given {@code fileContent} and returns a {@link List} within all profile {@link Node}s that were found in that {@code fileContent}.
*
* @param fileContent the file content to parse
*
* @return a {@link List} within all profile {@link Node}s that were found in the given {@code fileContent}
*
* @throws SAXException if there are parsing issues
* @throws IOException if there is an I/O issue
* @throws ConfigReadException if the given {@code fileContent} is not a valid Eclipse formatter template
*/
@NonNull
private static List<Node> getProfileNodes(String fileContent) throws ConfigReadException, IOException, SAXException {
if (fileContent == null) {
throw new ConfigReadException("fileContent cannot be null");
}
List<Node> profiles = new ArrayList<>();
Document formatterDoc;
try (StringReader reader = new StringReader(fileContent)) {
formatterDoc = XMLUtil.parse(new InputSource(reader), false, false, null, null);
}
Element profilesTag = formatterDoc.getDocumentElement();
if (profilesTag != null && TAG_NAME_PROFILES.equals(profilesTag.getNodeName())) {
NodeList profileTagList = profilesTag.getElementsByTagName(TAG_NAME_PROFILE);
if (profileTagList != null && profileTagList.getLength() > 0) {
for (int p = 0; p < profileTagList.getLength(); p++) {
Node profileTag = profileTagList.item(p);
NamedNodeMap profileAttributes = profileTag.getAttributes();
Node profileKindAttr = profileAttributes.getNamedItem(ATTRIBUTE_PROFILE_KIND);
if (profileKindAttr != null && PROFILE_KIND.equals(profileKindAttr.getNodeValue())) {
profiles.add(profileTag);
}
}
} else {
throw new ConfigReadException("No <profile> tag found in given file content");
}
} else {
throw new ConfigReadException("No <profiles> tag found in config file");
}
if (profiles.isEmpty()) {
throw new ConfigReadException("No valid <profile> tag found in given file content");
}
return profiles;
}
}