PropertyBag.java
/**
* Copyright (c) 2017-2018, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* SPDX-License-Identifier: MPL-2.0
*/
package com.powsybl.triplestore.api;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
/**
* @author Luma Zamarre��o {@literal <zamarrenolm at aia.es>}
*/
public class PropertyBag extends HashMap<String, String> {
public PropertyBag(List<String> propertyNames, boolean decodeEscapedIdentifiers) {
this(propertyNames, true, decodeEscapedIdentifiers);
}
public PropertyBag(List<String> propertyNames, boolean removeInitialUnderscoreForIdentifiers, boolean decodeEscapedIdentifiers) {
super(propertyNames.size());
this.propertyNames = propertyNames;
this.removeInitialUnderscoreForIdentifiers = removeInitialUnderscoreForIdentifiers;
this.decodeEscapedIdentifiers = decodeEscapedIdentifiers;
}
public List<String> propertyNames() {
return propertyNames;
}
public void putNonNull(String key, String value) {
if (key != null && value != null) {
put(key, value);
}
}
public String getLocal(String property) {
String value = get(property);
if (value == null) {
return null;
}
return extractIdentifier(value, false);
}
public String[] getLocals(String property, String separator) {
String value = get(property);
if (value == null) {
return new String[] {};
}
String[] tokens = value.split(separator);
for (int i = 0; i < tokens.length; i++) {
tokens[i] = extractIdentifier(tokens[i], false);
}
return tokens;
}
public String getId(String property) {
String value = get(property);
if (value == null) {
return null;
}
return extractIdentifier(value, true);
}
public String getId0(String property) {
// Return the first part of the Identifier (before the first hyphen)
String id = getId(property);
if (id == null) {
return null;
}
int h = id.indexOf('-');
if (h < 0) {
return id;
}
return id.substring(0, h);
}
public double asDouble(String property) {
return asDouble(property, Double.NaN);
}
public double asDouble(String property, double defaultValue) {
if (!containsKey(property)) {
return defaultValue;
}
try {
return Double.parseDouble(get(property));
} catch (NumberFormatException x) {
LOG.warn("Invalid value for property {} : {}", property, get(property));
return Double.NaN;
}
}
public Optional<Boolean> asBoolean(String property) {
if (!containsKey(property)) {
return Optional.empty();
}
return Optional.of(Boolean.parseBoolean(get(property)));
}
public boolean asBoolean(String property, boolean defaultValue) {
if (!containsKey(property)) {
return defaultValue;
}
return Boolean.parseBoolean(get(property));
}
public int asInt(String property) {
return Integer.parseInt(get(property));
}
public int asInt(String property, int defaultValue) {
if (!containsKey(property)) {
return defaultValue;
}
return Integer.parseInt(get(property));
}
public String tabulateLocals() {
return tabulate("", PropertyBag::getLocal);
}
public String tabulate() {
return tabulate("", PropertyBag::get);
}
public String tabulateLocals(String title) {
return tabulate(title, PropertyBag::getLocal);
}
public String tabulate(String title) {
return tabulate(title, HashMap::get);
}
private String tabulate(String title, BiFunction<PropertyBag, String, String> getValue) {
if (size() == 0) {
return "";
}
String lineSeparator = System.lineSeparator();
Optional<Integer> maxLenName = propertyNames.stream()
.map(String::length)
.max(Integer::compare);
if (maxLenName.isPresent()) {
int lenPad = maxLenName.get();
String format = String.format("%%-%ds", lenPad);
// Performance : avoid using concat() -> use a StringBuilder instead.
return new StringBuilder(title).append(lineSeparator).append(propertyNames.stream()
.map(n -> new StringBuilder(INDENTATION).append(String.format(format, n)).append(" : ").append(getValue.apply(this, n)).toString())
.collect(Collectors.joining(lineSeparator))).toString();
}
return "";
}
private String extractIdentifier(String s, boolean isIdentifier) {
String s1 = s;
int iHash = s.indexOf('#');
if (iHash >= 0) {
s1 = s.substring(iHash + 1);
}
// rdf:ID is the mRID plus an underscore added at the beginning of the string
// We may decide if we want to preserve or not the underscore
if (isIdentifier) {
if (removeInitialUnderscoreForIdentifiers && !s1.isEmpty() && s1.charAt(0) == '_') {
s1 = s1.substring(1);
}
if (decodeEscapedIdentifiers) {
s1 = URLDecoder.decode(s1, StandardCharsets.UTF_8);
}
}
return s1;
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), propertyNames, removeInitialUnderscoreForIdentifiers);
}
@Override
public final boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof PropertyBag p)) {
return false;
}
if (removeInitialUnderscoreForIdentifiers != p.removeInitialUnderscoreForIdentifiers) {
return false;
}
return propertyNames.equals(p.propertyNames);
}
public boolean isResource(String name) {
return RESOURCE_NAMES.contains(name) || resourceNames.contains(name);
}
public String namespacePrefix(String name) {
LOG.trace("namespacePrefix for property name {}", name);
return NAMESPACE_PREFIX;
}
public void setResourceNames(List<String> resourceNames) {
this.resourceNames.clear();
this.resourceNames.addAll(Objects.requireNonNull(resourceNames));
}
public void setClassPropertyNames(List<String> classPropertyNames) {
this.classPropertyNames.clear();
this.classPropertyNames.addAll(Objects.requireNonNull(classPropertyNames));
}
public boolean isClassProperty(String name) {
return classPropertyNames.contains(name);
}
public void setMultivaluedProperty(List<String> multiValuedPropertyNames) {
this.multiValuedPropertyNames.clear();
this.multiValuedPropertyNames.addAll(Objects.requireNonNull(multiValuedPropertyNames));
}
public boolean isMultivaluedProperty(String name) {
return multiValuedPropertyNames.contains(name);
}
public PropertyBag copy() {
// Create just a shallow copy of this property bag
PropertyBag pb1 = new PropertyBag(propertyNames, removeInitialUnderscoreForIdentifiers, decodeEscapedIdentifiers);
pb1.setResourceNames(resourceNames);
pb1.setClassPropertyNames(classPropertyNames);
pb1.setMultivaluedProperty(multiValuedPropertyNames);
pb1.putAll(this);
return pb1;
}
private final List<String> propertyNames;
private final boolean removeInitialUnderscoreForIdentifiers;
private final boolean decodeEscapedIdentifiers;
private final List<String> resourceNames = new ArrayList<>();
private final List<String> classPropertyNames = new ArrayList<>();
private final List<String> multiValuedPropertyNames = new ArrayList<>();
private static final String NAMESPACE_PREFIX = "data";
private static final String INDENTATION = " ";
private static final List<String> RESOURCE_NAMES = Arrays.asList("TopologicalNode", "Terminal", "ShuntCompensator",
"TapChanger", "ConductingEquipment", "Model.DependentOn", "TopologicalNodes",
"AngleRefTopologicalNode");
private static final Logger LOG = LoggerFactory.getLogger(PropertyBag.class);
}