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

com.aliyun.auth.credentials.utils.ProfileUtils Maven / Gradle / Ivy

The newest version!
package com.aliyun.auth.credentials.utils;

import com.aliyun.core.logging.ClientLogger;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.regex.Pattern;

/**
 * Only for internal use within the package, do not use it arbitrarily, backward compatibility and sustainability cannot be guaranteed.
 */
public class ProfileUtils {
    private static final ClientLogger logger = new ClientLogger(ProfileUtils.class);
    private static final Pattern EMPTY_LINE = Pattern.compile("^[\t ]*$");

    public static Map> parseFile(String profilePath) throws IOException {
        return parseFile(new FileReader(profilePath));
    }

    static Map> parseFile(Reader input) throws IOException {
        ParserProgress progress = new ParserProgress();
        BufferedReader profileReader = null;
        try {
            profileReader = new BufferedReader(input);
            String line;
            while ((line = profileReader.readLine()) != null) {
                parseLine(progress, line);
            }
        } finally {
            if (profileReader != null) {
                profileReader.close();
            }
        }
        return progress.profiles;
    }

    private static void parseLine(ParserProgress progress, String line) {
        ++progress.currentLineNumber;
        if (!EMPTY_LINE.matcher(line).matches() && !(line.startsWith("#") || line.startsWith(";"))) {
            if (isSectionDefinitionLine(line)) {
                readSectionDefinitionLine(progress, line);
            } else if (line.startsWith("\t")) {
                readPropertyContinuationLine(progress, line);
            } else {
                readPropertyDefinitionLine(progress, line);
            }
        }
    }

    private static void readSectionDefinitionLine(ParserProgress progress, String line) {
        String lineWithoutComments = removeTrailingComments(line, "#", ";");
        String lineWithoutWhitespace = lineWithoutComments.trim();

        if (!lineWithoutWhitespace.endsWith("]")) {
            throw new IllegalArgumentException(String.format("Section definition must end with ']' on line %s: %s", progress.currentLineNumber, line));
        }

        String lineWithoutBrackets = lineWithoutWhitespace.substring(1, lineWithoutWhitespace.length() - 1);
        String profileName = lineWithoutBrackets.trim();
        if (profileName.isEmpty()) {
            progress.ignoringCurrentProfile = true;
            return;
        }
        progress.currentProfileBeingRead = profileName;
        progress.currentPropertyBeingRead = null;
        progress.ignoringCurrentProfile = false;
        if (!progress.profiles.containsKey(profileName)) {
            progress.profiles.put(profileName, new LinkedHashMap());
        }
    }

    private static void readPropertyDefinitionLine(ParserProgress progress, String line) {
        // Invalid profile, ignore its properties
        if (progress.ignoringCurrentProfile) {
            return;
        }
        if (progress.currentProfileBeingRead == null) {
            // throw new IllegalArgumentException(String.format("Expected a profile definition on line %s", progress.currentLineNumber));
            // To be consistent with ini4j's behavior
            progress.currentProfileBeingRead = "?";
            if (!progress.profiles.containsKey(progress.currentProfileBeingRead)) {
                progress.profiles.put(progress.currentProfileBeingRead, new LinkedHashMap());
            }
        }

        // Comments with property must have whitespace before them, or they will be considered part of the value
        String lineWithoutComments = removeTrailingComments(line, " #", " ;", "\t#", "\t;");
        String lineWithoutWhitespace = lineWithoutComments.trim();
        Property property = parsePropertyDefinition(progress, lineWithoutWhitespace);

        if (progress.profiles.get(progress.currentProfileBeingRead).containsKey(property.key())) {
            logger.warning("Duplicate property '" + property.key() + "' detected on line " + progress.currentLineNumber +
                    ". The later one in the file will be used.");
        }

        progress.currentPropertyBeingRead = property.key();

        progress.profiles.get(progress.currentProfileBeingRead).put(property.key(), property.value());
    }

    private static void readPropertyContinuationLine(ParserProgress progress, String line) {
        // Invalid profile, ignore its properties
        if (progress.ignoringCurrentProfile) {
            return;
        }
        if (progress.currentProfileBeingRead == null) {
            // throw new IllegalArgumentException(String.format("Expected a profile definition on line %s", progress.currentLineNumber));
            // To be consistent with ini4j's behavior
            progress.currentProfileBeingRead = "?";
            if (!progress.profiles.containsKey(progress.currentProfileBeingRead)) {
                progress.profiles.put(progress.currentProfileBeingRead, new LinkedHashMap());
            }
        }

        // Comments are not removed on property continuation lines. They're considered part of the value.
        line = line.trim();
        Map profileProperties = progress.profiles.get(progress.currentProfileBeingRead);

        String currentPropertyValue = profileProperties.get(progress.currentPropertyBeingRead);
        String newPropertyValue = currentPropertyValue + "\n" + line;
        profileProperties.put(progress.currentPropertyBeingRead, newPropertyValue);
    }

    private static Property parsePropertyDefinition(ParserProgress progress, String line) {
        int firstEqualsLocation = line.indexOf('=');
        String propertyKey = null;
        String propertyValue = null;
        if (firstEqualsLocation == -1) {
            // throw new IllegalArgumentException(String.format("Expected an '=' sign defining a property on line %s", progress.currentLineNumber));
            // To be consistent with ini4j's behavior
            propertyKey = line.trim();
        } else {
            propertyKey = line.substring(0, firstEqualsLocation).trim();
            propertyValue = line.substring(firstEqualsLocation + 1).trim();
        }

        if (propertyKey.isEmpty()) {
            throw new IllegalArgumentException(String.format("Property did not have a name on line %s", progress.currentLineNumber));
        }

        return new Property(propertyKey, propertyValue);
    }

    private static boolean isSectionDefinitionLine(String line) {
        return line.trim().startsWith("[");
    }

    private static String removeTrailingComments(String line, String... commentPatterns) {
        int earliestMatchIndex = line.length();
        for (String pattern : commentPatterns) {
            int index = line.indexOf(pattern);
            if (index >= 0 && index < earliestMatchIndex) {
                earliestMatchIndex = index;
            }
        }
        return line.substring(0, earliestMatchIndex);
    }

    private static final class ParserProgress {
        private int currentLineNumber;
        private String currentProfileBeingRead;
        private String currentPropertyBeingRead;
        private boolean ignoringCurrentProfile;
        private final Map> profiles;

        private ParserProgress() {
            this.currentLineNumber = 0;
            this.currentProfileBeingRead = null;
            this.currentPropertyBeingRead = null;
            this.ignoringCurrentProfile = false;
            this.profiles = new LinkedHashMap>();
        }
    }

    private static final class Property {
        private final Key key;
        private final Value value;

        private Property(Key key, Value value) {
            this.key = key;
            this.value = value;
        }

        public Key key() {
            return this.key;
        }

        public Value value() {
            return this.value;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy