java properties 文件注释_【转】能保存Properties文件注释的Properties工具类

packagecom.vrv.util;importjava.io.BufferedWriter;importjava.io.File;importjava.io.FileInputStream;importjava.io.IOException;importjava.io.InputStream;importjava.io.InputStreamReader;importjava.io.OutputStream;importjava.io.OutputStreamWriter;importjava.io.Reader;importjava.io.Writer;importjava.util.Iterator;importjava.util.LinkedHashMap;importjava.util.Map;importjava.util.Properties;importjava.util.Set;/*** CommentedProperties

* 针对Properties进行扩展的工具类

*

* 扩展的两个主要功能:

* 1.对Properties文件中注释的保存。

* CommentedProperties在读取和保存Properties文件时,会将其注释

* 一起读取保存。CommentedProperties中会提供方法来根据key获取

* 相应注释。在CommentedProperties中添加一个K-V对时,也会提供

* 添加相应注释的方法。

*

* 2.对Properties文件中Key值顺序的保证。

* CommentedProperties会保证Key的顺序。从一个Properties文件中

* 读取所有K-V对,保存到另一个Properties文件时,Key的顺序不会

* 改变。

*

*

*@authorBrokenDreams*/

public classCommentedProperties{/*** 内部属性表*/

private finalProperties props;/*** 保存key与comment的映射,

* 同时利用这个映射来保证key的顺序。*/

private final LinkedHashMap keyCommentMap = new LinkedHashMap();private static final String BLANK = "";publicCommentedProperties() {super();

props= newProperties();

}publicCommentedProperties(Properties defaults) {super();

props= newProperties(defaults);

}/*** 设置一个属性,如果key已经存在,那么将其对应value值覆盖。

*@paramkey

*@paramvalue

*@return

*/

publicString setProperty(String key, String value) {returnsetProperty(key, value, BLANK);

}/*** 设置一个属性,如果key已经存在,那么将其对应value值覆盖。

*

*@paramkey 键

*@paramvalue 与键对应的值

*@paramcomment 对键值对的说明

*@return

*/

public synchronizedString setProperty(String key, String value, String comment){

Object oldValue=props.setProperty(key, value);if(BLANK.equals(comment)){if(!keyCommentMap.containsKey(key)){

keyCommentMap.put(key, comment);

}

}else{

keyCommentMap.put(key, comment);

}return(String)oldValue;

}/*** 根据key获取属性表中相应的value。

*

*@paramkey

*@return

*/

publicString getProperty(String key) {returnprops.getProperty(key);

}/*** 根据key获取属性表中相应的value。

* 如果没找到相应的value,返回defaultValue。

*

*@paramkey

*@paramdefaultValue

*@return

*/

publicString getProperty(String key, String defaultValue) {returnprops.getProperty(key, defaultValue);

}/*** 从一个字符流中读取属性到属性表中

*

*@paramreader

*@throwsIOException*/

public synchronized void load(Reader reader) throwsIOException {

load0(newLineReader(reader));

}/*** 从一个字节流中读取属性到属性表中

*

*@paraminStream

*@throwsIOException*/

public synchronized void load(InputStream inStream) throwsIOException {

load0(newLineReader(inStream));

}/*** 从一个字节流中读取属性到属性表中

*

*@paraminStream

*@paramcharset

*@throwsIOException*/

public synchronized void load(InputStream inStream, String charset) throwsIOException {

InputStreamReader reader= newInputStreamReader(inStream, charset);

load0(newLineReader(reader));

}/*** 从一个文件中读取属性到属性表中

*

*@paramfile 属性文件

*@paramcharset 字符集

*@throwsIOException*/

public synchronized void load(File file, String charset) throwsIOException {

FileInputStream inputStream= newFileInputStream(file);

InputStreamReader reader= newInputStreamReader(inputStream, charset);

load0(newLineReader(reader));

}/*** 从一个文件中读取属性到属性表中

* 默认字符集为utf-8

*

*@paramfile 属性文件

*@throwsIOException*/

public synchronized void load(File file) throwsIOException {

FileInputStream inputStream= newFileInputStream(file);

InputStreamReader reader= new InputStreamReader(inputStream, "utf-8");

load0(newLineReader(reader));

}/*** 将属性表中的属性写到字符流里面。

*

*@paramwriter

*@throwsIOException*/

public void store(Writer writer) throwsIOException {

store0((writerinstanceof BufferedWriter)?(BufferedWriter)writer

:new BufferedWriter(writer),false);

}/*** 将属性表中的属性写到字节流里面。

*

*@paramout

*@throwsIOException*/

public void store(OutputStream out) throwsIOException {

store0(new BufferedWriter(new OutputStreamWriter(out, "utf-8")),true);

}/*** 如果属性表中某个key对应的value值和参数value相同

* 那么返回true,否则返回false。

*

*@paramvalue

*@return

*/

public booleancontainsValue(String value) {returnprops.containsValue(value);

}/*** 如果属性表中存在参数key,返回true,否则返回false。

*

*@paramkey

*@return

*/

public booleancontainsKey(String key) {returnprops.containsKey(key);

}/*** 获取属性表中键值对数量

*@return

*/

public intsize() {returnprops.size();

}/*** 检查属性表是否为空

*@return

*/

public booleanisEmpty() {returnprops.isEmpty();

}/*** 清空属性表*/

public synchronized voidclear() {

props.clear();

keyCommentMap.clear();

}/*** 获取属性表中所有key的集合。

*

*@return

*/

public SetpropertyNames() {returnprops.stringPropertyNames();

}public synchronizedString toString() {

StringBuffer buffer= newStringBuffer();

Iterator> kvIter =keyCommentMap.entrySet().iterator();

buffer.append("[");while(kvIter.hasNext()){

buffer.append("{");

Map.Entry entry =kvIter.next();

String key=entry.getKey();

String val=getProperty(key);

String comment=entry.getValue();

buffer.append("key="+key+",value="+val+",comment="+comment);

buffer.append("}");

}

buffer.append("]");returnbuffer.toString();

}public booleanequals(Object o) {//不考虑注释说明是否相同

returnprops.equals(o);

}public inthashCode() {returnprops.hashCode();

}private void load0(LineReader lr) throwsIOException {char[] convtBuf = new char[1024];intlimit;intkeyLen;intvalueStart;charc;booleanhasSep;booleanprecedingBackslash;

StringBuffer buffer= newStringBuffer();while ((limit = lr.readLine()) >= 0) {

c= 0;

keyLen= 0;

valueStart=limit;

hasSep= false;//获取注释

c =lr.lineBuf[keyLen];if(c == '#' || c == '!'){

String comment= loadConvert(lr.lineBuf, 1, limit - 1, convtBuf);if(buffer.length() > 0){

buffer.append("\n");

}

buffer.append(comment);continue;

}

precedingBackslash= false;while (keyLen

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

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(key, value, buffer.toString());//reset buffer

buffer = newStringBuffer();

}

}/** 基于java.util.Properties.LineReader进行改造

*

* Read in a "logical line" from an InputStream/Reader, skip all comment

* and blank lines and filter out those leading whitespace characters

* (\u0020, \u0009 and \u000c) from the beginning of a "natural line".

* Method returns the char length of the "logical line" and stores

* the line in "lineBuf".*/

classLineReader {publicLineReader(InputStream inStream) {this.inStream =inStream;

inByteBuf= new byte[8192];

}publicLineReader(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() throwsIOException {int len = 0;char c = 0;boolean skipWhiteSpace = true;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) {return -1;

}returnlen;

}

}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 != '\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 (len == 0) {

isNewLine= true;

skipWhiteSpace= true;

len= 0;continue;

}if (inOff >=inLimit) {

inLimit= (inStream==null)?reader.read(inCharBuf)

:inStream.read(inByteBuf);

inOff= 0;if (inLimit <= 0) {returnlen;

}

}if(precedingBackslash) {

len-= 1;//skip the leading whitespace characters in following line

skipWhiteSpace = true;

appendedLineBegin= true;

precedingBackslash= false;if (c == '\r') {

skipLF= true;

}

}else{returnlen;

}

}

}

}

}/** Converts encoded \uxxxx to unicode chars

* and changes special saved chars to their original forms*/

private String loadConvert (char[] in, int off, int len, char[] convtBuf) {if (convtBuf.length

newLen=Integer.MAX_VALUE;

}

convtBuf= new char[newLen];

}charaChar;char[] out =convtBuf;int outLen = 0;int end = off +len;while (off

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 newIllegalArgumentException("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++] = (char)aChar;

}

}return new String (out, 0, outLen);

}private void store0(BufferedWriter bw, booleanescUnicode)throwsIOException{synchronized (this) {

Iterator> kvIter =keyCommentMap.entrySet().iterator();while(kvIter.hasNext()){

Map.Entry entry =kvIter.next();

String key=entry.getKey();

String val=getProperty(key);

String comment=entry.getValue();

key= saveConvert(key, true, escUnicode);/*No need to escape embedded and trailing spaces for value, hence

* pass false to flag.*/val= saveConvert(val, false, escUnicode);if(!comment.equals(BLANK))

writeComments(bw, comment);

bw.write(key+ "=" +val);

bw.newLine();

}

}

bw.flush();

}private static voidwriteComments(BufferedWriter bw, String comments)throwsIOException {

bw.write("#");int len =comments.length();int current = 0;int last = 0;while (current '\u00ff' || c == '\n' || c == '\r') {if (last !=current)

bw.write(comments.substring(last, current));if (c > '\u00ff') {

bw.write(c);

}else{

bw.newLine();if (c == '\r' &&current!= len - 1 &&comments.charAt(current+ 1) == '\n') {

current++;

}if (current == len - 1 ||(comments.charAt(current+ 1) != '#' &&comments.charAt(current+ 1) != '!'))

bw.write("#");

}

last= current + 1;

}

current++;

}if (last !=current)

bw.write(comments.substring(last, current));

bw.newLine();

}/** Converts unicodes to encoded \uxxxx and escapes

* special characters with a preceding slash*/

privateString saveConvert(String theString,booleanescapeSpace,booleanescapeUnicode) {int len =theString.length();int bufLen = len * 2;if (bufLen < 0) {

bufLen=Integer.MAX_VALUE;

}

StringBuffer outBuffer= newStringBuffer(bufLen);for(int x=0; x

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);

}

}

}returnoutBuffer.toString();

}/*** Convert a nibble to a hex character

*@paramnibble the nibble to convert.*/

private static char toHex(intnibble) {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'};

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值