001/*******************************************************************************
002The MIT License (MIT)
003
004Copyright (c) 2024 KILLCODING.COM
005
006Permission is hereby granted, free of charge, to any person obtaining a copy
007of this software and associated documentation files (the "Software"), to deal
008in the Software without restriction, including without limitation the rights
009to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
010copies of the Software, and to permit persons to whom the Software is
011furnished to do so, subject to the following conditions:
012
013The above copyright notice and this permission notice shall be included in
014all copies or substantial portions of the Software.
015
016THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
017IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
018FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
019AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
020LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
021OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
022THE SOFTWARE.
023*****************************************************************************/
024package com.killcoding.tool;
025
026import java.util.Properties;
027import java.util.regex.Pattern;
028import java.util.regex.Matcher;
029import java.util.Set;
030import java.util.Map;
031import java.util.Enumeration;
032import java.util.HashMap;
033import java.util.List;
034import java.util.ArrayList;
035import java.util.Collections;
036import java.util.Arrays;
037import com.killcoding.log.Logger;
038import java.io.StringReader;
039import java.io.Reader;
040import java.io.IOException;
041import java.io.InputStream;
042import java.io.InputStreamReader;
043import java.io.File;
044import java.io.FileInputStream;
045import java.util.Collection;
046import java.util.concurrent.ExecutorService;
047import java.util.concurrent.Executors;
048import java.nio.file.Files;
049import java.nio.file.Path;
050import java.nio.file.LinkOption;
051import java.nio.file.Paths;
052import java.net.URI;
053import java.text.SimpleDateFormat;
054import java.util.Date;
055import java.text.ParseException;
056import com.killcoding.file.BaseFile;
057import java.util.concurrent.CopyOnWriteArrayList;
058import com.killcoding.file.DiskFile;
059import com.killcoding.tool.CipherTools;
060import java.io.ByteArrayInputStream;
061
062public class ConfigProperties extends Properties {
063
064        public static final String ENV_VAR_REGEX = "[\\$%#]{1}\\{?([\\.\\w-]+)(:{1}([^\\}]+)){0,1}\\}";
065
066        public static String DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss.SSS";
067        
068        public static String Config_Cipher_AesKey = null;
069        
070    public static Long AUTO_LOAD_TIMER = 5000L;
071        private static final List<Runnable> AUTO_LOAD_CP = new CopyOnWriteArrayList<Runnable>();
072
073    private static ConfigCustomLoad CUSTOMLOAD = null;
074    
075        protected static ExecutorService executor = null;
076
077        private String parseContentRaw = null;
078
079        public ConfigProperties(int arg0) {
080                super(arg0);
081        }
082
083        public ConfigProperties(Properties arg0) {
084                super(arg0);
085        }
086
087        public ConfigProperties() {
088                super();
089        }
090
091        private void addAutoLoad(Runnable cpRun) {
092                AUTO_LOAD_CP.add(cpRun);
093                startAutoLoad();
094        }
095
096        private static synchronized void startAutoLoad() { 
097                if (executor == null) {
098                        executor = Executors.newFixedThreadPool(1);
099                        executor.execute(new Runnable() {
100                                @Override
101                                public void run() {
102                                    while(!Thread.currentThread().isInterrupted()){
103                                        Thread.currentThread().setName("ConfigProperties.startAutoLoad");
104                                        for (Runnable cpRun : AUTO_LOAD_CP) {
105                                            cpRun.run();
106                                        }                                       
107                                        try {
108                                                Thread.sleep(getAutoLoadTimer());
109                                        } catch (InterruptedException e) {
110                                                Logger.systemError(ConfigProperties.class, e.getMessage(), e);
111                                                break;
112                                        }                                       
113                                    }
114                                }
115                        });
116                }
117        }
118        
119        private static Long getAutoLoadTimer(){
120            return AUTO_LOAD_TIMER;
121        }
122
123        public static void setConfigCustom(ConfigCustomLoad customLoad){
124            ConfigProperties.CUSTOMLOAD =  customLoad;
125        }       
126        
127        private static boolean loadConfigCustom(File file,ConfigProperties target){
128            if(ConfigProperties.CUSTOMLOAD != null){
129            ConfigProperties.CUSTOMLOAD.load(file,target);
130            return true;
131            }
132            return false;
133        }
134
135        public synchronized void load(File file, Runnable updatedRunnable) throws IOException {
136                this.load(file);
137                final ConfigProperties the = this;
138                final Runnable cpRun = new Runnable() {
139                        @Override
140                        public void run() {
141                                Thread.currentThread().setName(String.format("AutoLoad-%s", file.getName()));
142                                try {
143                                        ConfigProperties target = new ConfigProperties();
144                                        if(Files.exists(Paths.get(file.getAbsolutePath()))){
145                                        target.load(file);
146                                        boolean isUseCustom = false;
147                                        boolean haveCustomLoad = loadConfigCustom(file,the);
148                                        if(haveCustomLoad){
149                                            isUseCustom = ConfigProperties.CUSTOMLOAD.isUseCustom();
150                                            boolean isUpdated = ConfigProperties.CUSTOMLOAD.isUpdated();
151                                            if(isUpdated){
152                                                        Logger.systemMark(ConfigProperties.class, "CustomUpdated.");
153                                                        if (updatedRunnable != null)
154                                                                updatedRunnable.run();
155                                            }
156                                        }
157                                        if(!isUseCustom){
158                                            boolean same = ConfigProperties.compareProperties(the, target);
159                                                if (!same) {
160                                                        the.load(file);
161                                                        Logger.systemMark(ConfigProperties.class, "Updated");
162                                                        Logger.systemDebug(ConfigProperties.class, the.parseContentRaw);
163                                                        
164                                                        if (updatedRunnable != null)
165                                                                updatedRunnable.run();
166                                                }   
167                                        }
168                                        }
169                                } catch (Exception e) {
170                                        Logger.systemError(ConfigProperties.class, e.getMessage(), e);
171                                }
172                        }
173                };
174                addAutoLoad(cpRun);
175        }
176
177        public void load(File file) throws IOException {
178            if(CommonTools.isBlank(Config_Cipher_AesKey)){
179                load(new FileInputStream(file));
180            }else{
181                try{
182                DiskFile df = new DiskFile(file);
183                byte[] encryptBytes = df.readAllBytes();
184                byte[] decryptBytes = CipherTools.AESDecrypt(Config_Cipher_AesKey,encryptBytes);
185                load(new ByteArrayInputStream(decryptBytes));
186                }catch(Exception e){
187                    throw new IOException(String.format("The file '%s' decryption failed.",file),e);
188                }
189            }
190        }
191
192        @Override
193        public void load(InputStream inputStream) throws IOException {
194                try {
195                        Reader reader = new InputStreamReader(inputStream,BaseFile.CHARSET);
196                        load(reader);
197                } finally {
198                        if (inputStream != null)
199                                inputStream.close();
200                }
201        }
202
203        @Override
204        public void load(Reader reader) throws IOException {
205                try {
206                        int readChar;
207                        StringBuffer sbf = new StringBuffer();
208                        while ((readChar = reader.read()) != -1) {
209                                sbf.append((char) readChar);
210                        }
211                        this.parseContentRaw = parseContent(sbf.toString());
212                        super.load(new StringReader(this.parseContentRaw));
213                } finally {
214                        if (reader != null)
215                                reader.close();
216                }
217        }
218
219        public ConfigProperties putAndReturnSlef(String key, Object value) {
220                if (isBlank(value)) {
221                        put(key, "");
222                } else if (value instanceof Map) {
223                        Map<String, Object> subMap = conver(key, (Map<String, Object>) value);
224                        putAllAndReturnSlef(subMap);
225                } else if (value instanceof List) {
226                        List<Object> list = (List<Object>) value;
227                        int size = list.size();
228                        for (int i = 0; i < size; i++) {
229                                Object item = list.get(i);
230                                String newKey = String.format("%s[%s]", key, i);
231                                putAndReturnSlef(newKey, item);
232                        }
233                } else if (value instanceof Object[]) {
234                        List<Object> list = Arrays.asList((Object[]) value);
235                        int size = list.size();
236                        for (int i = 0; i < size; i++) {
237                                Object item = list.get(i);
238                                String newKey = String.format("%s[%s]", key, i);
239                                putAndReturnSlef(newKey, item);
240                        }
241                } else if (value instanceof String) {
242                        put(key, value);
243                } else {
244                        put(key, value.toString());
245                }
246                return this;
247        }
248
249        public ConfigProperties removeAndReturnSlef(String key) {
250                this.remove(key);
251                return this;
252        }
253
254        public ConfigProperties putAllAndReturnSlef(Map<String, Object> map) {
255                Set<String> keys = map.keySet();
256                for (String key : keys) {
257                        Object value = map.get(key);
258                        putAndReturnSlef(key, value);
259                }
260                return this;
261        }
262
263        public void removeAllNullValues() {
264                Set<Object> keys = this.keySet();
265                for (Object key : keys) {
266                        Object value = get(key);
267                        boolean blank = isBlank(value);
268                        if (blank) {
269                                this.remove(key);
270                                continue;
271                        }
272                }
273        }
274
275        private Map<String, Object> conver(String parentKey, Map<String, Object> map) {
276                Map<String, Object> subMap = new HashMap<String, Object>();
277                Set<String> keys = map.keySet();
278                for (String key : keys) {
279                        String newKey = String.format("%s.%s", parentKey, key);
280                        Object value = map.get(key);
281                        subMap.put(newKey, value);
282                }
283                return subMap;
284        }
285
286        public Boolean getBoolean(String key) {
287                return getBoolean(key, null);
288        }
289
290        public Boolean getBoolean(String key, Boolean defaultValue) {
291            Object originValue = get(key);
292            
293            if(originValue == null) return defaultValue;
294            
295            if(originValue instanceof Boolean) return (Boolean)originValue;      
296            
297                String o = getString(key);
298                if (isBlank(o)) {
299                        return defaultValue;
300                }
301                return Boolean.parseBoolean(o.trim());
302        }
303
304        public List<String> getArray(String key) {
305                List<String> defaultValue = null;
306                List<String> v = getArray(key, defaultValue);
307                if (v == null) {
308                        String sv = getString(key);
309                        if (sv == null)
310                                return new ArrayList<String>();
311
312                        return new ArrayList<String>(Arrays.asList(new String[] { sv }));
313                } else {
314                        return v;
315                }
316        }
317
318        public List<String> getArray(String key, String[] defaultValue) {
319                return getArray(key, Arrays.asList(defaultValue));
320        }
321
322        public List<String> getArray(String key, List<String> defaultValue) {
323                String[] list = null;
324                Map<Integer, String> listMap = new HashMap<Integer, String>();
325                List<Integer> listIndex = new ArrayList<Integer>();
326                Set<Object> keys = keySet();
327                for (Object _key : keys) {
328                        String format = String.format("^%s\\[(\\w+)\\]$", key);
329                        Pattern pattern = Pattern.compile(format);
330                        Matcher matcher = pattern.matcher(_key.toString());
331                        if (matcher.find()) {
332                                Integer index = Integer.parseInt(matcher.group(1));
333                                String value = getString(_key.toString());
334                                listMap.put(index, value);
335                                listIndex.add(index);
336                        }
337                }
338                int listIndexSize = listIndex.size();
339                if (listIndexSize > 0) {
340                        Collections.sort(listIndex);
341                        list = new String[listIndex.get(listIndexSize - 1) + 1];
342                        for (int i = 0; i < listIndexSize; i++) {
343                                int index = listIndex.get(i);
344                                String value = listMap.get(index);
345                                list[index] = isBlank(value) ? null : value;
346                        }
347                        return Arrays.asList(list);
348                } else {
349                        return defaultValue;
350                }
351
352        }
353
354        public String getString(String key) {
355                return getString(key, null);
356        }
357
358        public String getString(String key, String defaultValue) {
359                String o = getProperty(key);
360                if (isBlank(o)) {
361                        return defaultValue;
362                }
363                return o.trim();
364        }
365
366        //      public String getString(String key, String defaultValue) {
367        //          String o = getProperty(key);
368        //          if(o == null)
369        //              return o;
370
371        //          String originValue = o.trim();     
372        //          Pattern p = Pattern.compile(ENV_VAR_REGEX);  
373        //         Matcher m = p.matcher(originValue);
374        //         boolean findEnvFormat = m.find();
375        //         if(findEnvFormat){
376        //             String envVar = m.group(1);
377        //             String envDefValue = m.group(3);
378        //             String envValue = getSysEnv(envVar);
379        //              if (!isBlank(envValue)) {
380        //                      return envValue;
381        //              }  
382        //              if (!isBlank(envDefValue)) {
383        //                      return envDefValue;
384        //              }               
385        //              return defaultValue;
386        //         }else{
387        //              if (isBlank(originValue)) {
388        //                      return defaultValue;
389        //              }
390        //              return originValue.trim();
391        //         }
392        //      }       
393
394        public Short getShort(String key) {
395                return getShort(key, null);
396        }
397
398        public Short getShort(String key, Short defaultValue) {
399            Object originValue = get(key);
400            
401            if(originValue == null) return defaultValue;
402            
403            if(originValue instanceof Number) return Short.parseShort(originValue + "");           
404            
405                String o = getString(key);
406                if (isBlank(o)) {
407                        return defaultValue;
408                }
409                return Short.parseShort(o.trim());
410        }
411
412        public Integer getInteger(String key) {
413                return getInteger(key, null);
414        }
415
416        public Integer getInteger(String key, Integer defaultValue) {
417            Object originValue = get(key);
418            
419            if(originValue == null) return defaultValue;
420            
421            if(originValue instanceof Number) return Integer.parseInt(originValue + "");
422            
423                String o = getString(key);
424                if (isBlank(o)) {
425                        return defaultValue;
426                }
427                return Integer.parseInt(o.trim());
428        }
429
430        public Long getLong(String key) {
431                return getLong(key, null);
432        }
433
434        public Long getLong(String key, Long defaultValue) {
435            Object originValue = get(key);
436            
437            if(originValue == null) return defaultValue;
438            
439            if(originValue instanceof Number) return Long.parseLong(originValue + "");      
440            
441                String o = getString(key);
442                if (isBlank(o)) {
443                        return defaultValue;
444                }
445                return Long.parseLong(o.trim());
446        }
447
448        public Float getFloat(String key) {
449                return getFloat(key, null);
450        }
451
452        public Float getFloat(String key, Float defaultValue) {
453            Object originValue = get(key);
454            
455            if(originValue == null) return defaultValue;
456            
457            if(originValue instanceof Number) return Float.parseFloat(originValue + "");            
458            
459                String o = getString(key);
460                if (isBlank(o)) {
461                        return defaultValue;
462                }
463                return Float.parseFloat(o.trim());
464        }
465
466        public Double getDouble(String key) {
467                return getDouble(key, null);
468        }
469
470        public Double getDouble(String key, Double defaultValue) {
471            Object originValue = get(key);
472            
473            if(originValue == null) return defaultValue;
474            
475            if(originValue instanceof Number) return Double.parseDouble(originValue + "");          
476            
477                String o = getString(key);
478                if (isBlank(o)) {
479                        return defaultValue;
480                }
481                return Double.parseDouble(o.trim());
482        }
483
484        public Long getSeconds(String key) {
485                return getSeconds(key, null);
486        }
487
488        public Long getSeconds(String key, Long defaultValue) {
489                return parseTimeToSec(getString(key, null), defaultValue);
490        }
491
492        public Long getMilliSeconds(String key) {
493                return getMilliSeconds(key, null);
494        }
495
496        public Long getMilliSeconds(String key, Long defaultValue) {
497                return parseTimeToMs(getString(key, null), defaultValue);
498        }
499
500        public Long getFileSize(String key, Long defaultValue) {
501                return parseFileSize(getString(key, null), defaultValue);
502        }
503        
504        public Date getDateTime(String key, String defaultValue) {
505                return parseDateTime(getString(key, null), defaultValue);
506        }
507        
508        public Date getDateTime(String key) {
509                return getDateTime(key,null);
510        }       
511
512        public ConfigProperties getConfigProperties(String key) {
513                Map<String, Object> map = getMap(key);
514                if (map != null) {
515                        ConfigProperties cp = new ConfigProperties();
516                        cp.putAllAndReturnSlef(map);
517                        return cp;
518                }
519                return null;
520        }
521
522        public ConfigProperties getConfigProperties(String key, ConfigProperties defaultValue) {
523                Map<String, Object> map = getMap(key);
524                if (map != null) {
525                        ConfigProperties cp = new ConfigProperties();
526                        cp.putAllAndReturnSlef(map);
527                        return cp;
528                }
529                return defaultValue;
530        }
531
532        public Map<String, Object> getMap(String key) {
533                return getMap(key, null);
534        }
535
536        public Map<String, Object> getMap(String key, Map<String, Object> defaultValue) {
537                Map<String, Object> cp = null;
538                Set<Object> keys = this.keySet();
539                for (Object keyObject : keys) {
540                        String _key = keyObject.toString();
541                        boolean isMap = _key.startsWith(String.format("%s.", key));
542                        if (isMap) {
543                                if (cp == null)
544                                        cp = new HashMap<String, Object>();
545
546                                String mapKey = _key.replaceFirst(String.format("^%s\\.", key), "");
547                                cp.put(mapKey, this.getString(_key, null));
548                        }
549                }
550                return isBlank(cp) ? defaultValue : cp;
551        }
552        
553        public String getContentRaw(){
554            return this.parseContentRaw;
555        }
556
557        public static boolean isBlank(Object o) {
558                return CommonTools.isBlank(o);
559        }
560
561        public static Long parseTimeToSec(Object objectValue, Long defaultValue) {
562
563                if (objectValue == null)
564                        return defaultValue;
565
566                String stringValue = null;
567                if (objectValue instanceof String) {
568                        stringValue = objectValue.toString().trim();
569                } else {
570                        return Long.parseLong(objectValue.toString().trim());
571                }
572
573                Long value = null;
574                try {
575                        Pattern pattern = Pattern.compile("^([-\\+]{0,1}[0-9]+)(((?i)ms|s|m|h|d|w){1})$");
576                        Matcher matcher = pattern.matcher(stringValue);
577                        if (matcher.find()) {
578                                Long originValue = Long.parseLong(matcher.group(1));
579                                String unit = matcher.group(2).toLowerCase();
580                                if (unit.equals("ms")) {
581                                        value = originValue / 1000;
582                                }
583                                if (unit.equals("s")) {
584                                        value = originValue;
585                                }
586                                if (unit.equals("m")) {
587                                        value = originValue * 60;
588                                }
589                                if (unit.equals("h")) {
590                                        value = originValue * 60 * 60;
591                                }
592                                if (unit.equals("d")) {
593                                        value = originValue * 60 * 60 * 24;
594                                }
595                                if (unit.equals("w")) {
596                                        value = originValue * 60 * 60 * 24 * 7;
597                                }
598                        } else {
599                                Logger.systemMark(ConfigProperties.class,
600                                                "Missing unit(ms|s|m|h|d|w) '{}' will using default value '{}', (e.g. {}ms,{}s,...).",
601                                                stringValue, defaultValue, stringValue, stringValue);
602                        }
603                        return value == null ? defaultValue : value;
604                } catch (Exception e) {
605                        Logger.systemError(ConfigProperties.class, e);
606                        return null;
607                }
608        }
609
610        public static Long parseTimeToMs(Object objectValue, Long defaultValue) {
611
612                if (objectValue == null)
613                        return defaultValue;
614
615                String stringValue = null;
616                if (objectValue instanceof String) {
617                        stringValue = objectValue.toString().trim();
618                } else {
619                        return Long.parseLong(objectValue.toString().trim());
620                }
621
622                Long value = null;
623                try {
624                        Pattern pattern = Pattern.compile("^([-\\+]{0,1}[0-9]+)(((?i)ms|s|m|h|d|w){1})$");
625                        Matcher matcher = pattern.matcher(stringValue);
626                        if (matcher.find()) {
627                                Long originValue = Long.parseLong(matcher.group(1));
628                                String unit = matcher.group(2).toLowerCase();
629                                if (unit.equals("ms")) {
630                                        value = originValue;
631                                }
632                                if (unit.equals("s")) {
633                                        value = originValue * 1000;
634                                }
635                                if (unit.equals("m")) {
636                                        value = originValue * 60 * 1000;
637                                }
638                                if (unit.equals("h")) {
639                                        value = originValue * 60 * 60 * 1000;
640                                }
641                                if (unit.equals("d")) {
642                                        value = originValue * 60 * 60 * 1000 * 24;
643                                }
644                                if (unit.equals("w")) {
645                                        value = originValue * 60 * 60 * 1000 * 24 * 7;
646                                }
647                        } else {
648                                Logger.systemMark(ConfigProperties.class,
649                                                "Missing unit(ms|s|m|h|d|w) '{}' will using default value '{}', (e.g. {}ms,{}s,...).",
650                                                stringValue, defaultValue, stringValue, stringValue);
651                        }
652                        return value == null ? defaultValue : value;
653                } catch (Exception e) {
654                        Logger.systemError(ConfigProperties.class, e);
655                        return null;
656                }
657        }
658        
659        public static Date parseDateTime(String objectValue, String defaultValue) {
660            SimpleDateFormat sf = new SimpleDateFormat(DATETIME_PATTERN);
661            try{
662            if(CommonTools.isBlank(objectValue)){
663                if(CommonTools.isBlank(defaultValue)) return null;
664                
665                if(!CommonTools.isBlank(defaultValue)) return sf.parse(defaultValue);
666            } 
667
668                return sf.parse(objectValue);
669            }catch(Exception e){
670                        Logger.systemError(ConfigProperties.class, e);
671                        return null;
672            }
673        }
674
675        public static Long parseFileSize(Object objectValue, Long defaultValue) {
676
677                if (objectValue == null)
678                        return defaultValue;
679
680                String stringValue = null;
681                if (objectValue instanceof String) {
682                        stringValue = objectValue.toString().trim();
683                } else {
684                        return Long.parseLong(objectValue.toString().trim());
685                }
686
687                Long value = null;
688                try {
689                        Pattern pattern = Pattern.compile("^([-\\+]{0,1}[0-9]+)(((?i)b|kb|mb|gb|tb){1})$");
690                        Matcher matcher = pattern.matcher(stringValue);
691                        if (matcher.find()) {
692                                Long originValue = Long.parseLong(matcher.group(1));
693                                String unit = matcher.group(2).toLowerCase();
694                                if (unit.equals("b")) {
695                                        value = originValue;
696                                }
697                                if (unit.equals("kb")) {
698                                        value = originValue * 1024;
699                                }
700                                if (unit.equals("mb")) {
701                                        value = originValue * 1024 * 1024;
702                                }
703                                if (unit.equals("gb")) {
704                                        value = originValue * 1024 * 1024 * 1024;
705                                }
706                                if (unit.equals("tb")) {
707                                        value = originValue * 1024 * 1024 * 1024 * 1024;
708                                }
709                        } else {
710                                Logger.systemMark(ConfigProperties.class,
711                                                "Missing unit(b|kb|mb|gb|tb) '{}' will using default value '{}', (e.g. {}mb,{}kb,...).",
712                                                stringValue, defaultValue, stringValue, stringValue);
713                        }
714                        return value == null ? defaultValue : value;
715                } catch (Exception e) {
716                        Logger.systemError(ConfigProperties.class, e);
717                        return null;
718                }
719        }
720
721        public static String getSysEnv(String envKey) {
722                return System.getenv(envKey) == null ? System.getProperty(envKey) : System.getenv(envKey);
723        }
724
725        public static String getEnv(String originValue) {
726                if (originValue == null)
727                        return null;
728
729                Pattern p = Pattern.compile(ENV_VAR_REGEX);
730                Matcher m = p.matcher(originValue.trim());
731                boolean findEnvFormat = m.find();
732                if (findEnvFormat) {
733                        String envVar = m.group(1);
734                        String envDefValue = m.group(3);
735                        String envValue = getSysEnv(envVar);
736                        if (!isBlank(envValue)) {
737                                return envValue;
738                        }
739                        if (!isBlank(envDefValue)) {
740                                return envDefValue;
741                        }
742                        return null;
743                }
744                return originValue;
745        }
746
747        public static String parseContent(String content) {
748                return parseContent(content, "");
749        }
750
751        public static String parseContent(String content, String replaceNullChar) {
752                Pattern p = Pattern.compile(ENV_VAR_REGEX, Pattern.DOTALL | Pattern.MULTILINE);
753                StringBuffer sb = new StringBuffer();
754                Matcher matcher = p.matcher(content);
755                while (matcher.find()) {
756                        String envValue = ConfigProperties.getEnv(matcher.group());
757                        envValue = (envValue == null ? replaceNullChar : envValue);
758                        matcher.appendReplacement(sb, envValue);
759                }
760                matcher.appendTail(sb);
761                return sb.toString();
762        }
763
764        public static boolean compareProperties(ConfigProperties props1, ConfigProperties props2) {
765
766                if (props1 == null || props2 == null)
767                        return false;
768
769                return props1.parseContentRaw.equals(props2.parseContentRaw);
770        }
771
772}