-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: remove ini4j && improve ini config file parsing
- Loading branch information
Showing
7 changed files
with
283 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
177 changes: 177 additions & 0 deletions
177
aliyun-java-sdk-core/src/main/java/com/aliyuncs/utils/ProfileUtils.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
package com.aliyuncs.utils; | ||
|
||
import org.apache.commons.logging.Log; | ||
import org.apache.commons.logging.LogFactory; | ||
|
||
import java.io.*; | ||
import java.util.LinkedHashMap; | ||
import java.util.Map; | ||
import java.util.regex.Pattern; | ||
|
||
public class ProfileUtils { | ||
private final static Log log = LogFactory.getLog(ProfileUtils.class); | ||
private static final Pattern EMPTY_LINE = Pattern.compile("^[\t ]*$"); | ||
|
||
public static Map<String, Map<String, String>> parseFile(String profilePath) throws IOException { | ||
return parseFile(new FileReader(profilePath)); | ||
} | ||
|
||
static Map<String, Map<String, String>> 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(" ") || 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<String, String>()); | ||
} | ||
} | ||
|
||
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)); | ||
} | ||
|
||
// 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<String, String> property = parsePropertyDefinition(progress, lineWithoutWhitespace); | ||
|
||
if (progress.profiles.get(progress.currentProfileBeingRead).containsKey(property.key())) { | ||
log.warn("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)); | ||
} | ||
|
||
// Comments are not removed on property continuation lines. They're considered part of the value. | ||
line = line.trim(); | ||
Map<String, String> 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<String, String> parsePropertyDefinition(ParserProgress progress, String line) { | ||
int firstEqualsLocation = line.indexOf('='); | ||
if (firstEqualsLocation == -1) { | ||
throw new IllegalArgumentException(String.format("Expected an '=' sign defining a property on line %s", progress.currentLineNumber)); | ||
} | ||
|
||
String propertyKey = line.substring(0, firstEqualsLocation).trim(); | ||
String 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<String, String>(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<String, Map<String, String>> profiles; | ||
|
||
private ParserProgress() { | ||
this.currentLineNumber = 0; | ||
this.currentProfileBeingRead = null; | ||
this.currentPropertyBeingRead = null; | ||
this.ignoringCurrentProfile = false; | ||
this.profiles = new LinkedHashMap<String, Map<String, String>>(); | ||
} | ||
} | ||
|
||
private static final class Property<Key, Value> { | ||
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; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
76 changes: 76 additions & 0 deletions
76
aliyun-java-sdk-core/src/test/java/com/aliyuncs/utils/ProfileUtilsTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package com.aliyuncs.utils; | ||
|
||
import org.ini4j.Wini; | ||
import org.junit.Assert; | ||
import org.junit.Test; | ||
|
||
import java.io.IOException; | ||
import java.io.StringReader; | ||
import java.util.Map; | ||
|
||
public class ProfileUtilsTest { | ||
@Test | ||
public void testProfile() throws IOException { | ||
String context = "[profile1]\n" | ||
+ ";comment\n" | ||
+ "#comment\n" | ||
+ "enable = false #comment\n" | ||
+ "[profile2]\n" | ||
+ "region = cn-hangzhou#comment\n" | ||
+ "[default]\n" | ||
+ "default_property = property1 \t\n\n" | ||
+ "[default]\n" | ||
+ "default_property = property2\n" | ||
+ "[profile3]\n" | ||
+ "int = 1\n" | ||
+ " int = 2\n" | ||
+ "int = \n" | ||
+ "\t3\n" | ||
+ "str = #comment\n" | ||
+ "\ttest\n"; | ||
Wini ini = new Wini(new StringReader(context)); | ||
Map<String, Map<String, String>> iniMap = ProfileUtils.parseFile(new StringReader(context)); | ||
Assert.assertEquals(4, ini.size()); | ||
Assert.assertEquals(4, iniMap.size()); | ||
Assert.assertEquals("false #comment", ini.get("profile1").get("enable")); | ||
Assert.assertEquals("false", iniMap.get("profile1").get("enable")); | ||
Assert.assertEquals(ini.get("profile2").get("region"), iniMap.get("profile2").get("region")); | ||
Assert.assertEquals(ini.get("default").get("default_property"), iniMap.get("default").get("default_property")); | ||
Assert.assertEquals("\n3", iniMap.get("profile3").get("int")); | ||
Assert.assertEquals("\ntest", iniMap.get("profile3").get("str")); | ||
|
||
context = "[profile1]\n" | ||
+ "enable = false\n" | ||
+ "[profile2]\n" | ||
+ "enable = true\n" | ||
+ "[profile3]\n" | ||
+ "enable = null\n" | ||
+ "[profile4]\n" | ||
+ "enable = 1\n" | ||
+ "[profile5]\n" | ||
+ "enable = False\n" | ||
+ "[profile6]\n" | ||
+ "enable = True\n" | ||
+ "[profile7]\n" | ||
+ "enable =\n"; | ||
ini = new Wini(new StringReader(context)); | ||
iniMap = ProfileUtils.parseFile(new StringReader(context)); | ||
Assert.assertEquals(7, ini.size()); | ||
Assert.assertEquals(7, iniMap.size()); | ||
Assert.assertEquals(false, ini.get("profile1").get("enable", Boolean.class)); | ||
Assert.assertEquals(false, Boolean.parseBoolean(iniMap.get("profile1").get("enable"))); | ||
Assert.assertEquals(true, ini.get("profile2").get("enable", Boolean.class)); | ||
Assert.assertEquals(true, Boolean.parseBoolean(iniMap.get("profile2").get("enable"))); | ||
Assert.assertEquals(false, ini.get("profile3").get("enable", Boolean.class)); | ||
Assert.assertEquals(false, Boolean.parseBoolean(iniMap.get("profile3").get("enable"))); | ||
Assert.assertEquals(false, ini.get("profile4").get("enable", Boolean.class)); | ||
Assert.assertEquals(false, Boolean.parseBoolean(iniMap.get("profile4").get("enable"))); | ||
Assert.assertEquals(false, ini.get("profile5").get("enable", Boolean.class)); | ||
Assert.assertEquals(false, Boolean.parseBoolean(iniMap.get("profile5").get("enable"))); | ||
Assert.assertEquals(true, ini.get("profile6").get("enable", Boolean.class)); | ||
Assert.assertEquals(true, Boolean.parseBoolean(iniMap.get("profile6").get("enable"))); | ||
Assert.assertEquals(false, ini.get("profile7").get("enable", Boolean.class)); | ||
Assert.assertEquals(false, Boolean.parseBoolean(iniMap.get("profile7").get("enable"))); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters