Source Code Cross Referenced for FormatPreservingProperties.java in  » Database-ORM » openjpa » org » apache » openjpa » lib » util » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Database ORM » openjpa » org.apache.openjpa.lib.util 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*
002:         * Licensed to the Apache Software Foundation (ASF) under one
003:         * or more contributor license agreements.  See the NOTICE file
004:         * distributed with this work for additional information
005:         * regarding copyright ownership.  The ASF licenses this file
006:         * to you under the Apache License, Version 2.0 (the
007:         * "License"); you may not use this file except in compliance
008:         * with the License.  You may obtain a copy of the License at
009:         *
010:         * http://www.apache.org/licenses/LICENSE-2.0
011:         *
012:         * Unless required by applicable law or agreed to in writing,
013:         * software distributed under the License is distributed on an
014:         * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015:         * KIND, either express or implied.  See the License for the
016:         * specific language governing permissions and limitations
017:         * under the License.    
018:         */
019:        package org.apache.openjpa.lib.util;
020:
021:        import java.io.BufferedReader;
022:        import java.io.FilterInputStream;
023:        import java.io.IOException;
024:        import java.io.InputStream;
025:        import java.io.InputStreamReader;
026:        import java.io.ObjectInputStream;
027:        import java.io.ObjectOutputStream;
028:        import java.io.OutputStream;
029:        import java.io.OutputStreamWriter;
030:        import java.io.PrintWriter;
031:        import java.io.Serializable;
032:        import java.util.Calendar;
033:        import java.util.Collection;
034:        import java.util.HashSet;
035:        import java.util.Iterator;
036:        import java.util.LinkedHashSet;
037:        import java.util.LinkedList;
038:        import java.util.List;
039:        import java.util.Map;
040:        import java.util.Properties;
041:        import java.util.Set;
042:
043:        /*
044:         * ### things to add: - should probably be a SourceTracker
045:         * - if an entry is removed, should there be an option to remove comments
046:         * just before the entry(a la javadoc)?
047:         * - should we have an option to clean up whitespace?
048:         * - potentially would be interesting to add comments about each
049:         * property that OpenJPA adds to this object. We'd want to make the
050:         * automatic comment-removing code work first, though, so that if
051:         * someone then removed the property, the comments would go away.
052:         * - would be neat if DuplicateKeyException could report line numbers of
053:         * offending entries.
054:         * - putAll() with another FormatPreservingProperties should be smarter
055:         */
056:
057:        /**
058:         * A specialization of {@link Properties} that stores its contents
059:         * in the same order and with the same formatting as was used to read
060:         * the contents from an input stream. This is useful because it means
061:         * that a properties file loaded via this object and then written
062:         * back out later on will only be different where changes or
063:         * additions were made.
064:         * By default, the {@link #store} method in this class does not
065:         * behave the same as {@link Properties#store}. You can cause an
066:         * instance to approximate the behavior of {@link Properties#store}
067:         * by invoking {@link #setDefaultEntryDelimiter} with <code>=</code>,
068:         * {@link #setAddWhitespaceAfterDelimiter} with <code>false</code>, and
069:         * {@link #setAllowDuplicates} with <code>true</code>. However, this
070:         * will only influence how the instance will write new values, not how
071:         * it will write existing key-value pairs that are modified.
072:         * In conjunction with a conservative output writer, it is
073:         * possible to only write to disk changes / additions.
074:         * This implementation does not permit escaped ' ', '=', ':'
075:         * characters in key names.
076:         *
077:         * @since 0.3.3
078:         */
079:        public class FormatPreservingProperties extends Properties {
080:
081:            private static Localizer _loc = Localizer
082:                    .forPackage(FormatPreservingProperties.class);
083:
084:            private char defaultEntryDelimiter = ':';
085:            private boolean addWhitespaceAfterDelimiter = true;
086:            private boolean allowDuplicates = false;
087:            private boolean insertTimestamp = false;
088:
089:            private PropertySource source;
090:            private LinkedHashSet newKeys = new LinkedHashSet();
091:            private HashSet modifiedKeys = new HashSet();
092:
093:            // marker that indicates that we're not deserializing
094:            private transient boolean isNotDeserializing = true;
095:            private transient boolean isLoading = false;
096:
097:            public FormatPreservingProperties() {
098:                this (null);
099:            }
100:
101:            public FormatPreservingProperties(Properties defaults) {
102:                super (defaults);
103:            }
104:
105:            /**
106:             * The character to use as a delimiter between property keys and values.
107:             *
108:             * @param defaultEntryDelimiter either ':' or '='
109:             */
110:            public void setDefaultEntryDelimiter(char defaultEntryDelimiter) {
111:                this .defaultEntryDelimiter = defaultEntryDelimiter;
112:            }
113:
114:            /**
115:             * See {@link #setDefaultEntryDelimiter}
116:             */
117:            public char getDefaultEntryDelimiter() {
118:                return this .defaultEntryDelimiter;
119:            }
120:
121:            /**
122:             * If set to <code>true</code>, this properties object will add a
123:             * space after the delimiter character(if the delimiter is not
124:             * the space character). Else, this will not add a space.
125:             * Default value: <code>true</code>. Note that {@link
126:             * Properties#store} never writes whitespace.
127:             */
128:            public void setAddWhitespaceAfterDelimiter(boolean add) {
129:                this .addWhitespaceAfterDelimiter = add;
130:            }
131:
132:            /**
133:             * If set to <code>true</code>, this properties object will add a
134:             * space after the delimiter character(if the delimiter is not
135:             * the space character). Else, this will not add a space.
136:             * Default value: <code>true</code>. Note that {@link
137:             * Properties#store} never writes whitespace.
138:             */
139:            public boolean getAddWhitespaceAfterDelimiter() {
140:                return this .addWhitespaceAfterDelimiter;
141:            }
142:
143:            /**
144:             * If set to <code>true</code>, this properties object will add a
145:             * timestamp to the beginning of the file, just after the header
146:             * (if any) is printed. Else, this will not add a timestamp.
147:             * Default value: <code>false</code>. Note that {@link
148:             * Properties#store} always writes a timestamp.
149:             */
150:            public void setInsertTimestamp(boolean insertTimestamp) {
151:                this .insertTimestamp = insertTimestamp;
152:            }
153:
154:            /**
155:             * If set to <code>true</code>, this properties object will add a
156:             * timestamp to the beginning of the file, just after the header
157:             * (if any) is printed. Else, this will not add a timestamp.
158:             * Default value: <code>false</code>. Note that {@link
159:             * Properties#store} always writes a timestamp.
160:             */
161:            public boolean getInsertTimestamp() {
162:                return this .insertTimestamp;
163:            }
164:
165:            /**
166:             * If set to <code>true</code>, duplicate properties are allowed, and
167:             * the last property setting in the input will overwrite any previous
168:             * settings. If set to <code>false</code>, duplicate property definitions
169:             * in the input will cause an exception to be thrown during {@link #load}.
170:             * Default value: <code>false</code>. Note that {@link
171:             * Properties#store} always allows duplicates.
172:             */
173:            public void setAllowDuplicates(boolean allowDuplicates) {
174:                this .allowDuplicates = allowDuplicates;
175:            }
176:
177:            /**
178:             * If set to <code>true</code>, duplicate properties are allowed, and
179:             * the last property setting in the input will overwrite any previous
180:             * settings. If set to <code>false</code>, duplicate property definitions
181:             * in the input will cause an exception to be thrown during {@link #load}.
182:             * Default value: <code>false</code>. Note that {@link
183:             * Properties#store} always allows duplicates.
184:             */
185:            public boolean getAllowDuplicates() {
186:                return this .allowDuplicates;
187:            }
188:
189:            public String getProperty(String key) {
190:                return super .getProperty(key);
191:            }
192:
193:            public String getProperty(String key, String defaultValue) {
194:                return super .getProperty(key, defaultValue);
195:            }
196:
197:            public Object setProperty(String key, String value) {
198:                return put(key, value);
199:            }
200:
201:            /**
202:             * Circumvents the superclass {@link #putAll} implementation,
203:             * putting all the key-value pairs via {@link #put}.
204:             */
205:            public void putAll(Map m) {
206:                Map.Entry e;
207:                for (Iterator iter = m.entrySet().iterator(); iter.hasNext();) {
208:                    e = (Map.Entry) iter.next();
209:                    put(e.getKey(), e.getValue());
210:                }
211:            }
212:
213:            /**
214:             * Removes the key from the bookkeeping collectiotns as well.
215:             */
216:            public Object remove(Object key) {
217:                newKeys.remove(key);
218:                return super .remove(key);
219:            }
220:
221:            public void clear() {
222:                super .clear();
223:
224:                if (source != null)
225:                    source.clear();
226:
227:                newKeys.clear();
228:                modifiedKeys.clear();
229:            }
230:
231:            public Object clone() {
232:                FormatPreservingProperties c = (FormatPreservingProperties) super 
233:                        .clone();
234:
235:                if (source != null)
236:                    c.source = (PropertySource) source.clone();
237:
238:                if (modifiedKeys != null)
239:                    c.modifiedKeys = (HashSet) modifiedKeys.clone();
240:
241:                if (newKeys != null) {
242:                    c.newKeys = new LinkedHashSet();
243:                    c.newKeys.addAll(newKeys);
244:                }
245:
246:                return c;
247:            }
248:
249:            private void writeObject(ObjectOutputStream out) throws IOException {
250:                out.defaultWriteObject();
251:            }
252:
253:            private void readObject(ObjectInputStream in) throws IOException,
254:                    ClassNotFoundException {
255:                in.defaultReadObject();
256:
257:                isNotDeserializing = true;
258:            }
259:
260:            public Object put(Object key, Object val) {
261:                Object o = super .put(key, val);
262:
263:                // if we're no longer loading from properties and this put
264:                // represents an actual change in value, mark the modification
265:                // or addition in the bookkeeping collections.
266:                if (!isLoading && isNotDeserializing && !val.equals(o)) {
267:                    if (o != null)
268:                        modifiedKeys.add(key);
269:                    else if (!newKeys.contains(key))
270:                        newKeys.add(key);
271:                }
272:                return o;
273:            }
274:
275:            /**
276:             * Loads the properties in <code>in</code>, according to the rules
277:             * described in {@link Properties#load}. If {@link #getAllowDuplicates}
278:             * returns <code>true</code>, this will throw a {@link
279:             * DuplicateKeyException} if duplicate property declarations are
280:             * encountered.
281:             *
282:             * @see Properties#load
283:             */
284:            public void load(InputStream in) throws IOException {
285:                isLoading = true;
286:                try {
287:                    loadProperties(in);
288:                } finally {
289:                    isLoading = false;
290:                }
291:            }
292:
293:            private void loadProperties(InputStream in) throws IOException {
294:                source = new PropertySource();
295:
296:                PropertyLineReader reader = new PropertyLineReader(in, source);
297:
298:                Set loadedKeys = new HashSet();
299:
300:                for (PropertyLine l; (l = reader.readPropertyLine()) != null
301:                        && source.add(l);) {
302:                    String line = l.line.toString();
303:
304:                    char c = 0;
305:                    int pos = 0;
306:
307:                    while (pos < line.length() && isSpace(c = line.charAt(pos)))
308:                        pos++;
309:
310:                    if ((line.length() - pos) == 0 || line.charAt(pos) == '#'
311:                            || line.charAt(pos) == '!')
312:                        continue;
313:
314:                    StringBuffer key = new StringBuffer();
315:                    while (pos < line.length()
316:                            && !isSpace(c = line.charAt(pos++)) && c != '='
317:                            && c != ':') {
318:                        if (c == '\\') {
319:                            if (pos == line.length()) {
320:                                l.append(line = reader.readLine());
321:                                pos = 0;
322:                                while (pos < line.length()
323:                                        && isSpace(c = line.charAt(pos)))
324:                                    pos++;
325:                            } else {
326:                                pos = readEscape(line, pos, key);
327:                            }
328:                        } else {
329:                            key.append(c);
330:                        }
331:                    }
332:
333:                    boolean isDelim = (c == ':' || c == '=');
334:
335:                    for (; pos < line.length() && isSpace(c = line.charAt(pos)); pos++)
336:                        ;
337:
338:                    if (!isDelim && (c == ':' || c == '=')) {
339:                        pos++;
340:                        while (pos < line.length()
341:                                && isSpace(c = line.charAt(pos)))
342:                            pos++;
343:                    }
344:
345:                    StringBuffer element = new StringBuffer(line.length() - pos);
346:
347:                    while (pos < line.length()) {
348:                        c = line.charAt(pos++);
349:                        if (c == '\\') {
350:                            if (pos == line.length()) {
351:                                l.append(line = reader.readLine());
352:
353:                                if (line == null)
354:                                    break;
355:
356:                                pos = 0;
357:                                while (pos < line.length()
358:                                        && isSpace(c = line.charAt(pos)))
359:                                    pos++;
360:                                element.ensureCapacity(line.length() - pos
361:                                        + element.length());
362:                            } else {
363:                                pos = readEscape(line, pos, element);
364:                            }
365:                        } else
366:                            element.append(c);
367:                    }
368:
369:                    if (!loadedKeys.add(key.toString()) && !allowDuplicates)
370:                        throw new DuplicateKeyException(key.toString(),
371:                                getProperty(key.toString()), element.toString());
372:
373:                    l.setPropertyKey(key.toString());
374:                    l.setPropertyValue(element.toString());
375:                    put(key.toString(), element.toString());
376:                }
377:            }
378:
379:            /**
380:             * Read the next escaped character: handle newlines, tabs, returns, and
381:             * form feeds with the appropriate escaped character, then try to
382:             * decode unicode characters. Finally, just add the character explicitly.
383:             *
384:             * @param source the source of the characters
385:             * @param pos the position at which to start reading
386:             * @param value the value we are appending to
387:             * @return the position after the reading is done
388:             */
389:            private static int readEscape(String source, int pos,
390:                    StringBuffer value) {
391:                char c = source.charAt(pos++);
392:                switch (c) {
393:                case 'n':
394:                    value.append('\n');
395:                    break;
396:                case 't':
397:                    value.append('\t');
398:                    break;
399:                case 'f':
400:                    value.append('\f');
401:                    break;
402:                case 'r':
403:                    value.append('\r');
404:                    break;
405:                case 'u':
406:                    if (pos + 4 <= source.length()) {
407:                        char uni = (char) Integer.parseInt(source.substring(
408:                                pos, pos + 4), 16);
409:                        value.append(uni);
410:                        pos += 4;
411:                    }
412:                    break;
413:                default:
414:                    value.append(c);
415:                    break;
416:                }
417:
418:                return pos;
419:            }
420:
421:            private static boolean isSpace(char ch) {
422:                return Character.isWhitespace(ch);
423:            }
424:
425:            public void save(OutputStream out, String header) {
426:                try {
427:                    store(out, header);
428:                } catch (IOException ex) {
429:                }
430:            }
431:
432:            public void store(OutputStream out, String header)
433:                    throws IOException {
434:                boolean endWithNewline = source != null && source.endsInNewline;
435:
436:                // Must be ISO-8859-1 ecoding according to Properties.load javadoc
437:                PrintWriter writer = new PrintWriter(new OutputStreamWriter(
438:                        out, "ISO-8859-1"), false);
439:
440:                if (header != null)
441:                    writer.println("#" + header);
442:
443:                if (insertTimestamp)
444:                    writer.println("#" + Calendar.getInstance().getTime());
445:
446:                List lines = new LinkedList();
447:                // first write all the existing props as they were initially read
448:                if (source != null)
449:                    lines.addAll(source);
450:
451:                // next write out new keys, then the rest of the keys
452:                LinkedHashSet keys = new LinkedHashSet();
453:                keys.addAll(newKeys);
454:                keys.addAll(keySet());
455:
456:                lines.addAll(keys);
457:
458:                keys.remove(null);
459:
460:                boolean needsNewline = false;
461:
462:                for (Iterator i = lines.iterator(); i.hasNext();) {
463:                    Object next = i.next();
464:
465:                    if (next instanceof  PropertyLine) {
466:                        if (((PropertyLine) next).write(writer, keys,
467:                                needsNewline))
468:                            needsNewline = i.hasNext();
469:                    } else if (next instanceof  String) {
470:                        String key = (String) next;
471:                        if (keys.remove(key)) {
472:                            if (writeProperty(key, writer, needsNewline)) {
473:                                needsNewline = i.hasNext() && keys.size() > 0;
474:
475:                                // any new or modified properties will cause
476:                                // the file to end with a newline
477:                                endWithNewline = true;
478:                            }
479:                        }
480:                    }
481:                }
482:
483:                // make sure we end in a newline if the source ended in it
484:                if (endWithNewline)
485:                    writer.println();
486:
487:                writer.flush();
488:            }
489:
490:            private boolean writeProperty(String key, PrintWriter writer,
491:                    boolean needsNewline) {
492:                StringBuffer s = new StringBuffer();
493:
494:                if (key == null)
495:                    return false;
496:
497:                String val = getProperty(key);
498:                if (val == null)
499:                    return false;
500:
501:                formatValue(key, s, true);
502:                s.append(defaultEntryDelimiter);
503:                if (addWhitespaceAfterDelimiter)
504:                    s.append(' ');
505:                formatValue(val, s, false);
506:
507:                if (needsNewline)
508:                    writer.println();
509:
510:                writer.print(s);
511:
512:                return true;
513:            }
514:
515:            /**
516:             * Format the given string as an encoded value for storage. This will
517:             * perform any necessary escaping of special characters.
518:             *
519:             * @param str the value to encode
520:             * @param buf the buffer to which to append the encoded value
521:             * @param isKey if true, then the string is a Property key, otherwise
522:             * it is a value
523:             */
524:            private static void formatValue(String str, StringBuffer buf,
525:                    boolean isKey) {
526:                if (isKey) {
527:                    buf.setLength(0);
528:                    buf.ensureCapacity(str.length());
529:                } else {
530:                    buf.ensureCapacity(buf.length() + str.length());
531:                }
532:
533:                boolean escapeSpace = true;
534:                int size = str.length();
535:
536:                for (int i = 0; i < size; i++) {
537:                    char c = str.charAt(i);
538:
539:                    if (c == '\n')
540:                        buf.append("\\n");
541:                    else if (c == '\r')
542:                        buf.append("\\r");
543:                    else if (c == '\t')
544:                        buf.append("\\t");
545:                    else if (c == '\f')
546:                        buf.append("\\f");
547:                    else if (c == ' ')
548:                        buf.append(escapeSpace ? "\\ " : " ");
549:                    else if (c == '\\' || c == '!' || c == '#' || c == '='
550:                            || c == ':')
551:                        buf.append('\\').append(c);
552:                    else if (c < ' ' || c > '~')
553:                        buf.append(
554:                                "\\u0000".substring(0, 6 - Integer.toHexString(
555:                                        c).length())).append(
556:                                Integer.toHexString(c));
557:                    else
558:                        buf.append(c);
559:
560:                    if (c != ' ')
561:                        escapeSpace = isKey;
562:                }
563:            }
564:
565:            public static class DuplicateKeyException extends RuntimeException {
566:
567:                public DuplicateKeyException(String key, Object firstVal,
568:                        String secondVal) {
569:                    super (_loc.get("dup-key", key, firstVal, secondVal)
570:                            .getMessage());
571:                }
572:            }
573:
574:            /**
575:             * Contains the original line of the properties file: can be a
576:             * proper key/value pair, or a comment, or just whitespace.
577:             */
578:            private class PropertyLine implements  Serializable {
579:
580:                private final StringBuffer line = new StringBuffer();
581:                private String propertyKey;
582:                private String propertyValue;
583:
584:                public PropertyLine(String line) {
585:                    this .line.append(line);
586:                }
587:
588:                public void append(String newline) {
589:                    line.append(J2DoPrivHelper.getLineSeparator());
590:                    line.append(newline);
591:                }
592:
593:                public void setPropertyKey(String propertyKey) {
594:                    this .propertyKey = propertyKey;
595:                }
596:
597:                public String getPropertyKey() {
598:                    return this .propertyKey;
599:                }
600:
601:                public void setPropertyValue(String propertyValue) {
602:                    this .propertyValue = propertyValue;
603:                }
604:
605:                public String getPropertyValue() {
606:                    return this .propertyValue;
607:                }
608:
609:                /**
610:                 * Write the given line. It will only be written if the line is a
611:                 * comment, or if it is a property and its value is unchanged
612:                 * from the original.
613:                 *
614:                 * @param pw the PrintWriter to which the write
615:                 * @return whether or not this was a known key
616:                 */
617:                public boolean write(PrintWriter pw, Collection keys,
618:                        boolean needsNewline) {
619:                    // no property? It may be a comment or just whitespace
620:                    if (propertyKey == null) {
621:                        if (needsNewline)
622:                            pw.println();
623:                        pw.print(line.toString());
624:                        return true;
625:                    }
626:
627:                    // check to see if we are the same value we initially read:
628:                    // if so, then just write it back exactly as it was read
629:                    if (propertyValue != null
630:                            && containsKey(propertyKey)
631:                            && (propertyValue.equals(getProperty(propertyKey)) || (!newKeys
632:                                    .contains(propertyKey) && !modifiedKeys
633:                                    .contains(propertyKey)))) {
634:                        if (needsNewline)
635:                            pw.println();
636:                        pw.print(line.toString());
637:
638:                        keys.remove(propertyKey);
639:
640:                        return true;
641:                    }
642:
643:                    // if we have modified or added the specified key, then write
644:                    // it back to the same location in the file from which it
645:                    // was originally read, so that it will be in the proximity
646:                    // to the comment
647:                    if (containsKey(propertyKey)
648:                            && (modifiedKeys.contains(propertyKey) || newKeys
649:                                    .contains(propertyKey))) {
650:                        while (keys.remove(propertyKey))
651:                            ;
652:                        return writeProperty(propertyKey, pw, needsNewline);
653:                    }
654:
655:                    // this is a new or changed property: don't do anything
656:                    return false;
657:                }
658:            }
659:
660:            private class PropertyLineReader extends BufferedReader {
661:
662:                public PropertyLineReader(InputStream in, PropertySource source)
663:                        throws IOException {
664:                    // Must be ISO-8859-1 ecoding according to Properties.load javadoc
665:                    super (new InputStreamReader(
666:                            new LineEndingStream(in, source), "ISO-8859-1"));
667:                }
668:
669:                public PropertyLine readPropertyLine() throws IOException {
670:                    String l = readLine();
671:                    if (l == null)
672:                        return null;
673:
674:                    PropertyLine pl = new PropertyLine(l);
675:                    return pl;
676:                }
677:            }
678:
679:            /**
680:             * Simple FilterInputStream that merely remembers if the last
681:             * character that it read was a newline or not.
682:             */
683:            private static class LineEndingStream extends FilterInputStream {
684:
685:                private final PropertySource source;
686:
687:                LineEndingStream(InputStream in, PropertySource source) {
688:                    super (in);
689:
690:                    this .source = source;
691:                }
692:
693:                public int read() throws IOException {
694:                    int c = super .read();
695:                    source.endsInNewline = (c == '\n' || c == '\r');
696:                    return c;
697:                }
698:
699:                public int read(byte[] b, int off, int len) throws IOException {
700:                    int n = super .read(b, off, len);
701:                    if (n > 0)
702:                        source.endsInNewline = (b[n + off - 1] == '\n' || b[n
703:                                + off - 1] == '\r');
704:                    return n;
705:                }
706:            }
707:
708:            static class PropertySource extends LinkedList implements 
709:                    Cloneable, Serializable {
710:
711:                private boolean endsInNewline = false;
712:            }
713:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.