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

cn.featherfly.common.io.PropertiesImpl Maven / Gradle / Ivy


package cn.featherfly.common.io;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Collectors;

import cn.featherfly.common.constant.Chars;
import cn.featherfly.common.lang.Dates;
import cn.featherfly.common.lang.Lang;
import cn.featherfly.common.lang.Strings;

/**
 * 

* PropertiesImpl *

* * @author zhongj */ public class PropertiesImpl implements Properties { private Charset charset = StandardCharsets.UTF_8; private Map partMap = new LinkedHashMap<>(); private Properties defaults; /** * Creates an empty property list with no default values. */ public PropertiesImpl() { super(); } /** * Creates an empty property list with the specified defaults. * * @param defaults the defaults. */ public PropertiesImpl(Properties defaults) { this(defaults.getCharset()); this.defaults = defaults; int i = 1; for (Part part : defaults.listAll()) { if (part instanceof Property) { partMap.put(((Property) part).getKey(), part); } else { partMap.put("comments[" + i + "]", part); i++; } } } /** * Creates an empty property list with no default values. * * @param charset charset */ public PropertiesImpl(Charset charset) { super(); if (charset != null) { this.charset = charset; } } /** * Creates an empty property list with no default values. * * @param properties properties */ public PropertiesImpl(java.util.Properties properties) { this(properties, null); } /** * Creates an empty property list with no default values. * * @param properties java.util.Properties * @param charset charset */ public PropertiesImpl(java.util.Properties properties, Charset charset) { this(charset); if (properties != null) { properties.stringPropertyNames().forEach(pn -> { setProperty(pn, properties.getProperty(pn)); }); } } /** * {@inheritDoc} */ @Override public String setProperty(String key, String value) { return setProperty(key, value, null); } /** * {@inheritDoc} */ @Override public String setProperty(String key, String value, String comment) { Property property = setProperty(new Property(key, value, comment)); return property == null ? null : property.getValue(); } /** * {@inheritDoc} */ @Override public Property setProperty(Property property) { return (Property) partMap.put(property.getKey(), property); } /** * {@inheritDoc} */ @Override public String getProperty(String key) { Property p = getPropertyPart(key); if (Lang.isNotEmpty(p)) { return p.getValue(); } return null; } /** * {@inheritDoc} */ @Override public String getProperty(String key, String defaultValue) { String val = getProperty(key); return val == null ? defaultValue : val; } /** * {@inheritDoc} */ @Override public Property getPropertyPart(String key) { Property p = (Property) partMap.get(key); if (p == null && defaults != null) { p = defaults.getPropertyPart(key); } return p; } /** * {@inheritDoc} */ @Override public Collection getPropertyParts() { return partMap.values().stream().filter(p -> p instanceof Property).map(p -> (Property) p) .collect(Collectors.toList()); } /** * {@inheritDoc} */ @Override public Collection getPropertyNames() { return partMap.keySet(); } // /** // * {@inheritDoc} // */ // @Override // public Properties comment(String... comment) { // for (String c : comment) { // partMap.put(UUIDGenerator.generateUUID22Letters(), new Comment(c)); // } // return this; // } private String addComment(String comment) { String key = System.currentTimeMillis() + ""; partMap.put(key, new Comment(comment)); return key; } /** * {@inheritDoc} */ @Override public Collection listAll() { return partMap.values(); } /** * {@inheritDoc} */ @Override public Charset getCharset() { return charset; } /** * {@inheritDoc} */ @Override public java.util.Properties toJdkProperties() { java.util.Properties properties = new java.util.Properties(); getPropertyParts().forEach(p -> properties.put(p.getKey(), p.getValue())); return properties; } /** * {@inheritDoc} */ @Override public void store(OutputStream out) throws IOException { store0(new BufferedWriter(new OutputStreamWriter(out, charset)), charset == StandardCharsets.ISO_8859_1, charset); // if (charset == StandardCharsets.ISO_8859_1) { // // store0(new BufferedWriter(new OutputStreamWriter(out, "8859_1")), comments, true); // store0(new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.ISO_8859_1)), true, // StandardCharsets.ISO_8859_1); // } else { // store0(new BufferedWriter(new OutputStreamWriter(out, charset)), false, charset); // } } /** * {@inheritDoc} */ @Override public void load(InputStream is) throws IOException { load0(new LineReader(is)); } private void load0(LineReader lr) throws IOException { char[] convtBuf = new char[1024]; int limit; int keyLen; int valueStart; char c; boolean hasSep; boolean precedingBackslash; String comment = null; String commentKey = null; boolean firstLine = true; while ((limit = lr.readLine()) >= 0) { c = 0; keyLen = 0; valueStart = limit; hasSep = false; precedingBackslash = false; // 处理注释 if (Chars.SHARP_CHAR == lr.lineBuf[0] || Chars.NOT_L_CHAR == lr.lineBuf[0]) { comment = loadConvert(lr.lineBuf, 0, limit, convtBuf).substring(1); if (Lang.isNotEmpty(comment)) { comment = Strings.encode(comment, StandardCharsets.ISO_8859_1, charset); CharsetComment cc; if (firstLine && (cc = CharsetComment.createIfCan(comment)) != null) { charset = cc.getCharset(); firstLine = false; // charsetFromFile = true; continue; } commentKey = addComment(comment); firstLine = false; continue; } } firstLine = false; while (keyLen < limit) { c = lr.lineBuf[keyLen]; //need check if escaped. if ((c == '=' || c == ':') && !precedingBackslash) { valueStart = keyLen + 1; hasSep = true; break; } else if ((c == ' ' || c == '\t' || c == '\f') && !precedingBackslash) { valueStart = keyLen + 1; break; } if (c == '\\') { precedingBackslash = !precedingBackslash; } else { precedingBackslash = false; } keyLen++; } while (valueStart < limit) { c = lr.lineBuf[valueStart]; if (c != ' ' && c != '\t' && c != '\f') { if (!hasSep && (c == '=' || c == ':')) { hasSep = true; } else { break; } } valueStart++; } String key = loadConvert(lr.lineBuf, 0, keyLen, convtBuf); String value = loadConvert(lr.lineBuf, valueStart, limit - valueStart, convtBuf); setProperty(Strings.encode(key, StandardCharsets.ISO_8859_1, charset), Strings.encode(value, StandardCharsets.ISO_8859_1, charset), comment); partMap.remove(commentKey); comment = null; } } private String loadConvert(char[] in, int off, int len, char[] convtBuf) { if (convtBuf.length < len) { int newLen = len * 2; if (newLen < 0) { newLen = Integer.MAX_VALUE; } convtBuf = new char[newLen]; } char aChar; char[] out = convtBuf; int outLen = 0; int end = off + len; while (off < end) { aChar = in[off++]; if (aChar == '\\') { aChar = in[off++]; if (aChar == 'u') { // Read the xxxx int value = 0; for (int i = 0; i < 4; i++) { aChar = in[off++]; switch (aChar) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': value = (value << 4) + aChar - '0'; break; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': value = (value << 4) + 10 + aChar - 'a'; break; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': value = (value << 4) + 10 + aChar - 'A'; break; default: throw new IllegalArgumentException("Malformed \\uxxxx encoding."); } } out[outLen++] = (char) value; } else { if (aChar == 't') { aChar = '\t'; } else if (aChar == 'r') { aChar = '\r'; } else if (aChar == 'n') { aChar = '\n'; } else if (aChar == 'f') { aChar = '\f'; } out[outLen++] = aChar; } } else { out[outLen++] = aChar; } } return new String(out, 0, outLen); } private void store0(BufferedWriter bw, boolean escUnicode, Charset charset) throws IOException { bw.write(Chars.SHARP + "charset=" + charset.displayName() + Chars.NEW_LINE); bw.write(Chars.SHARP + "updated at " + Dates.formatTime(new Date()) + Chars.NEW_LINE); // bw.newLine(); synchronized (this) { for (Part part : listAll()) { if (part instanceof Property) { Property property = (Property) part; String key = saveConvert(property.getKey(), true, escUnicode); String val = saveConvert(property.getValue(), false, escUnicode); // String comment = saveConvert(property.getComment(), false, escUnicode); Property np = new Property(key, val, Lang.ifNotEmpty(property.getComment(), comm -> saveConvert(comm, false, escUnicode), () -> "")); bw.write(np.toPart()); bw.write(Chars.NEW_LINE); // bw.newLine(); } else { Comment nc = new Comment(); if (charset == null || charset == StandardCharsets.ISO_8859_1) { nc.setComment(Strings.stringToUnicode(((Comment) part).getComment())); } else { nc.setComment(((Comment) part).getComment()); } bw.write(nc.toPart()); bw.write(Chars.NEW_LINE); // bw.newLine(); } } } bw.flush(); } private String saveConvert(String theString, boolean escapeSpace, boolean escapeUnicode) { int len = theString.length(); int bufLen = len * 2; if (bufLen < 0) { bufLen = Integer.MAX_VALUE; } StringBuffer outBuffer = new StringBuffer(bufLen); for (int x = 0; x < len; x++) { char aChar = theString.charAt(x); // Handle common case first, selecting largest block that // avoids the specials below if (aChar > 61 && aChar < 127) { if (aChar == '\\') { outBuffer.append('\\'); outBuffer.append('\\'); continue; } outBuffer.append(aChar); continue; } switch (aChar) { case ' ': if (x == 0 || escapeSpace) { outBuffer.append('\\'); } outBuffer.append(' '); break; case '\t': outBuffer.append('\\'); outBuffer.append('t'); break; case '\n': outBuffer.append('\\'); outBuffer.append('n'); break; case '\r': outBuffer.append('\\'); outBuffer.append('r'); break; case '\f': outBuffer.append('\\'); outBuffer.append('f'); break; case '=': // Fall through case ':': // Fall through case '#': // Fall through case '!': outBuffer.append('\\'); outBuffer.append(aChar); break; default: if ((aChar < 0x0020 || aChar > 0x007e) & escapeUnicode) { outBuffer.append('\\'); outBuffer.append('u'); outBuffer.append(toHex(aChar >> 12 & 0xF)); outBuffer.append(toHex(aChar >> 8 & 0xF)); outBuffer.append(toHex(aChar >> 4 & 0xF)); outBuffer.append(toHex(aChar & 0xF)); } else { outBuffer.append(aChar); } } } return outBuffer.toString(); } class LineReader { public LineReader(InputStream inStream) { this.inStream = inStream; inByteBuf = new byte[8192]; } public LineReader(Reader reader) { this.reader = reader; inCharBuf = new char[8192]; } byte[] inByteBuf; char[] inCharBuf; char[] lineBuf = new char[1024]; int inLimit = 0; int inOff = 0; InputStream inStream; Reader reader; int readLine() throws IOException { int len = 0; char c = 0; boolean skipWhiteSpace = true; boolean isCommentLine = false; boolean isNewLine = true; boolean appendedLineBegin = false; boolean precedingBackslash = false; boolean skipLF = false; while (true) { if (inOff >= inLimit) { inLimit = inStream == null ? reader.read(inCharBuf) : inStream.read(inByteBuf); inOff = 0; if (inLimit <= 0) { if (len == 0 || isCommentLine) { return -1; } if (precedingBackslash) { len--; } return len; } } if (inStream != null) { //The line below is equivalent to calling a //ISO8859-1 decoder. c = (char) (0xff & inByteBuf[inOff++]); } else { c = inCharBuf[inOff++]; } if (skipLF) { skipLF = false; if (c == '\n') { continue; } } if (skipWhiteSpace) { if (c == ' ' || c == '\t' || c == '\f') { continue; } if (!appendedLineBegin && (c == '\r' || c == '\n')) { continue; } skipWhiteSpace = false; appendedLineBegin = false; } if (isNewLine) { isNewLine = false; if (c == '#' || c == '!') { // 取消掉忽略注释 // isCommentLine = true; // continue; } } if (c != '\n' && c != '\r') { lineBuf[len++] = c; if (len == lineBuf.length) { int newLength = lineBuf.length * 2; if (newLength < 0) { newLength = Integer.MAX_VALUE; } char[] buf = new char[newLength]; System.arraycopy(lineBuf, 0, buf, 0, lineBuf.length); lineBuf = buf; } //flip the preceding backslash flag if (c == '\\') { precedingBackslash = !precedingBackslash; } else { precedingBackslash = false; } } else { // reached EOL if (isCommentLine || len == 0) { isCommentLine = false; isNewLine = true; skipWhiteSpace = true; len = 0; continue; } if (inOff >= inLimit) { inLimit = inStream == null ? reader.read(inCharBuf) : inStream.read(inByteBuf); inOff = 0; if (inLimit <= 0) { if (precedingBackslash) { len--; } return len; } } if (precedingBackslash) { len -= 1; //skip the leading whitespace characters in following line skipWhiteSpace = true; appendedLineBegin = true; precedingBackslash = false; if (c == '\r') { skipLF = true; } } else { return len; } } } } } /** * Convert a nibble to a hex character * * @param nibble the nibble to convert. */ private static char toHex(int nibble) { return hexDigit[nibble & 0xF]; } /** A table of hex digits */ private static final char[] hexDigit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy