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

org.update4j.util.PropertyManager Maven / Gradle / Ivy

/*
 * Copyright 2018 Mordechai Meisels
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * 		http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package org.update4j.util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import org.update4j.OS;
import org.update4j.PlaceholderMatchType;
import org.update4j.Property;

public class PropertyManager {

	public static final Pattern PLACEHOLDER = Pattern.compile("\\$\\{([^}]+)\\}");

	private List properties;
	private List unmodifiableProperties;

	private Map resolvedProperties;
	private Map unmodifiableResolvedProperties;

	public PropertyManager(List properties, Map dynamicProperties,
					List systemProperties) {

		this.properties = properties == null ? new ArrayList<>() : properties;
		this.unmodifiableProperties = Collections.unmodifiableList(this.properties);

		for (int i = 1; i < this.properties.size(); i++) {
			for (int j = 0; j < i; j++) {
				Property ip = properties.get(i);
				Property jp = properties.get(j);

				if (ip.getKey().equals(jp.getKey()) && ip.getOs() == jp.getOs()) {
					throw new IllegalArgumentException("Duplicate property: " + ip.getKey());
				}
			}
		}

		if (systemProperties != null) {
			for (int i = 1; i < systemProperties.size(); i++) {
				for (int j = 0; j < i; j++) {
					if (systemProperties.get(i).equals(systemProperties.get(j)))
						throw new IllegalArgumentException("Duplicate system property: " + systemProperties.get(i));
				}
			}
		}

		resolvedProperties = extractPropertiesForCurrentMachine(this.properties, systemProperties);

		if (dynamicProperties != null)
			resolvedProperties.putAll(dynamicProperties);

		resolvedProperties = resolveDependencies(resolvedProperties);
		unmodifiableResolvedProperties = Collections.unmodifiableMap(resolvedProperties);
	}

	/**
	 * Returns an unmodifiable list of properties listed in the configuration file.
	 * This will never return {@code null}.
	 * 
	 * 

* This is read from the {@code } element. * * @return The {@link Property} instances listed in the configuration file. */ public List getProperties() { return unmodifiableProperties; } /** * Returns a list of properties listed in the configuration file that have the * provided key. It might be more than one, if they have different operating * systems. The list will never contain 2 properties with the same value * returned by {@link Property#getOs()}. * *

* The list might be empty, but never {@code null}. * * * @return The {@link Property} instances listed in the configuration file that * contain the provided key. */ public List getProperties(String key) { return getProperties().stream().filter(p -> p.getKey().equals(key)).collect(Collectors.toList()); } /** * Returns an unmodifiable map of keys and values after resolving the * placeholders. It includes everything from dynamic properties to system * properties or environment variables. This will not include properties marked * for foreign operating systems. * * @return A map of the keys and real values of the properties, after resolving * the placeholders. */ public Map getResolvedProperties() { return unmodifiableResolvedProperties; } /** * Returns the real value of the property with the given key, after resolving * the placeholders. It includes everything from dynamic properties to system * properties or environment variables. * * @param key * The key of the property. * @return The real value of the property after resolving the placeholders. */ public String getResolvedProperty(String key) { return resolvedProperties.get(key); } /** * Returns a string where all placeholders are replaced with the real values. * *

* If it includes a reference to a foreign property that could not be resolved * (as if that property refers to a system dependent system property), the * placeholder will not be replaced. * * @param str * The source string to try to resolve. * @return The resolved string. * @throws IllegalArgumentException * if the source string contains a placeholder that could not be * resolved. */ public String resolvePlaceholders(String str) { return resolvePlaceholders(str, false); } public String resolvePlaceholders(String str, boolean isPath) { return resolvePlaceholders(str, isPath, false); } /** * ignoreForeignProperty will not throw an exception if the key is found in an * unresolved foreign property. */ public String resolvePlaceholders(String str, boolean isPath, boolean ignoreForeignProperty) { if (str == null) { return null; } Matcher match = PLACEHOLDER.matcher(str); while (match.find()) { String key = match.group(1); String value = resolvedProperties.get(key); if (value == null) { Property prop = getProperties(key).stream().findAny().orElse(null); if (prop != null && prop.getOs() != null && prop.getOs() != OS.CURRENT && ignoreForeignProperty) { continue; } value = trySystemProperty(key); resolvedProperties.put(key, value); } str = str.replace(wrap(key), value); } if (isPath) str = str.replace("\\", "/"); return str; } public String implyPlaceholders(String str) { return implyPlaceholders(str, false); } public String implyPlaceholders(String str, boolean isPath) { return implyPlaceholders(str, PlaceholderMatchType.WHOLE_WORD, isPath); } public String implyPlaceholders(String str, PlaceholderMatchType matchType) { return implyPlaceholders(str, matchType, false); } public String implyPlaceholders(String str, PlaceholderMatchType matchType, boolean isPath) { if (str == null) { return null; } Objects.requireNonNull(matchType); if (isPath) { str = str.replace("\\", "/"); } if (matchType == PlaceholderMatchType.NONE) { return str; } // Get a list sorted by longest value List> resolved = resolvedProperties.entrySet() .stream() .filter(e -> !e.getValue().isEmpty()) .sorted((e1, e2) -> e2.getValue().length() - e1.getValue().length()) .peek(e -> { if (isPath) { e.setValue(e.getValue().replace("\\", "/")); } }) .collect(Collectors.toList()); for (Map.Entry e : resolved) { if (str.equals(e.getValue())) { return wrap(e.getKey()); } } // should've matched in for-loop above if (matchType == PlaceholderMatchType.FULL_MATCH) { return str; } /* * https://stackoverflow.com/a/34464459/1751640 * * This regex will not replace characters inside an existing placeholder. */ if (matchType == PlaceholderMatchType.EVERY_OCCURRENCE) { for (Map.Entry e : resolved) { String pattern = "(? e : resolved) { String pattern = "(? extractPropertiesForCurrentMachine(Collection properties, Collection systemProperties) { Map resolved = new HashMap<>(); if (systemProperties != null) { for (String sysProp : systemProperties) { resolved.put(sysProp, trySystemProperty(sysProp, true)); } } if (properties != null) { for (Property prop : properties) { // First resolve non os-specific, so the latter can override if (prop.getOs() != null) continue; resolved.put(prop.getKey(), prop.getValue()); } for (Property prop : properties) { // Overrides any non os-specific property if (prop.getOs() != OS.CURRENT) continue; resolved.put(prop.getKey(), prop.getValue()); } } return resolved; } /* * https://stackoverflow.com/a/1347594 */ private static Map resolveDependencies(Map properties) { Map noDeps = new HashMap<>(); while (properties.size() > 0) { int noDepsSize = noDeps.size(); List> found = new ArrayList<>(); Iterator> iter = properties.entrySet().iterator(); while (iter.hasNext()) { Map.Entry entry = iter.next(); if (!PLACEHOLDER.matcher(entry.getValue()).find()) { iter.remove(); found.add(entry); noDeps.put(entry.getKey(), entry.getValue()); } } if (noDepsSize == noDeps.size()) { // No changes boolean foundSystem = false; String key = null; for (String val : properties.values()) { Matcher match = PLACEHOLDER.matcher(val); match.find(); key = match.group(1); if (!properties.containsKey(key)) { String sys = trySystemProperty(key); noDeps.put(key, sys); found.add(Map.entry(key, sys)); foundSystem = true; } } if (!foundSystem) { throw new IllegalStateException("Cyclic property detected: " + key); } } for (Map.Entry entry : properties.entrySet()) { for (Map.Entry f : found) { entry.setValue(entry.getValue().replace(wrap(f.getKey()), f.getValue())); } } } return noDeps; } /* * Resolves first system property, falls back to environment variable. */ private static String trySystemProperty(String key, boolean systemInError) { String value = System.getProperty(key, System.getenv(key)); if (value != null) { if (PLACEHOLDER.matcher(value).find()) { throw new IllegalStateException("System properties must not contain placeholders."); } return value; } else { throw new IllegalArgumentException( "Could not resolve " + (systemInError ? "system " : "") + "property '" + key + "'"); } } private static String trySystemProperty(String key) { return trySystemProperty(key, false); } public static String wrap(String key) { return "${" + key + "}"; } public static boolean containsPlaceholder(String str) { return PLACEHOLDER.matcher(str).find(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy