Source Code Cross Referenced for StrSubstitutor.java in  » Library » Apache-common-lang » org » apache » commons » lang » text » 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 » Library » Apache common lang » org.apache.commons.lang.text 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*
002:         * Licensed to the Apache Software Foundation (ASF) under one or more
003:         * contributor license agreements.  See the NOTICE file distributed with
004:         * this work for additional information regarding copyright ownership.
005:         * The ASF licenses this file to You under the Apache License, Version 2.0
006:         * (the "License"); you may not use this file except in compliance with
007:         * the License.  You may obtain a copy of the License at
008:         * 
009:         *      http://www.apache.org/licenses/LICENSE-2.0
010:         * 
011:         * Unless required by applicable law or agreed to in writing, software
012:         * distributed under the License is distributed on an "AS IS" BASIS,
013:         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014:         * See the License for the specific language governing permissions and
015:         * limitations under the License.
016:         */
017:        package org.apache.commons.lang.text;
018:
019:        import java.util.ArrayList;
020:        import java.util.List;
021:        import java.util.Map;
022:
023:        /**
024:         * Substitutes variables within a string by values.
025:         * <p>
026:         * This class takes a piece of text and substitutes all the variables within it.
027:         * The default definition of a variable is <code>${variableName}</code>.
028:         * The prefix and suffix can be changed via constructors and set methods.
029:         * <p>
030:         * Variable values are typically resolved from a map, but could also be resolved
031:         * from system properties, or by supplying a custom variable resolver.
032:         * <p>
033:         * The simplest example is to use this class to replace Java System properties. For example:
034:         * <pre>
035:         * StrSubstitutor.replaceSystemProperties(
036:         *      "You are running with java.version = ${java.version} and os.name = ${os.name}.");
037:         * </pre>
038:         * <p>
039:         * Typical usage of this class follows the following pattern: First an instance is created
040:         * and initialized with the map that contains the values for the available variables.
041:         * If a prefix and/or suffix for variables should be used other than the default ones,
042:         * the appropriate settings can be performed. After that the <code>replace()</code>
043:         * method can be called passing in the source text for interpolation. In the returned
044:         * text all variable references (as long as their values are known) will be resolved.
045:         * The following example demonstrates this:
046:         * <pre>
047:         * Map valuesMap = HashMap();
048:         * valuesMap.put(&quot;animal&quot;, &quot;quick brown fox&quot;);
049:         * valuesMap.put(&quot;target&quot;, &quot;lazy dog&quot;);
050:         * String templateString = &quot;The ${animal} jumped over the ${target}.&quot;;
051:         * StrSubstitutor sub = new StrSubstitutor(valuesMap);
052:         * String resolvedString = sub.replace(templateString);
053:         * </pre>
054:         * yielding:
055:         * <pre>
056:         *      The quick brown fox jumped over the lazy dog.
057:         * </pre>
058:         * <p>
059:         * In addition to this usage pattern there are some static convenience methods that
060:         * cover the most common use cases. These methods can be used without the need of
061:         * manually creating an instance. However if multiple replace operations are to be
062:         * performed, creating and reusing an instance of this class will be more efficient.
063:         * <p>
064:         * Variable replacement works in a recursive way. Thus, if a variable value contains
065:         * a variable then that variable will also be replaced. Cyclic replacements are
066:         * detected and will cause an exception to be thrown.
067:         * <p>
068:         * Sometimes the interpolation's result must contain a variable prefix. As an example
069:         * take the following source text:
070:         * <pre>
071:         *   The variable ${${name}} must be used.
072:         * </pre>
073:         * Here only the variable's name refered to in the text should be replaced resulting
074:         * in the text (assuming that the value of the <code>name</code> variable is <code>x</code>):
075:         * <pre>
076:         *   The variable ${x} must be used.
077:         * </pre>
078:         * To achieve this effect there are two possibilities: Either set a different prefix
079:         * and suffix for variables which do not conflict with the result text you want to
080:         * produce. The other possibility is to use the escape character, by default '$'.
081:         * If this character is placed before a variable reference, this reference is ignored
082:         * and won't be replaced. For example:
083:         * <pre>
084:         *   The variable $${${name}} must be used.
085:         * </pre>
086:         *
087:         * @author Oliver Heger
088:         * @author Stephen Colebourne
089:         * @version $Id: StrSubstitutor.java 437554 2006-08-28 06:21:41Z bayard $
090:         * @since 2.2
091:         */
092:        public class StrSubstitutor {
093:
094:            /**
095:             * Constant for the default escape character.
096:             */
097:            public static final char DEFAULT_ESCAPE = '$';
098:            /**
099:             * Constant for the default variable prefix.
100:             */
101:            public static final StrMatcher DEFAULT_PREFIX = StrMatcher
102:                    .stringMatcher("${");
103:            /**
104:             * Constant for the default variable suffix.
105:             */
106:            public static final StrMatcher DEFAULT_SUFFIX = StrMatcher
107:                    .stringMatcher("}");
108:
109:            /**
110:             * Stores the escape character.
111:             */
112:            private char escapeChar;
113:            /**
114:             * Stores the variable prefix.
115:             */
116:            private StrMatcher prefixMatcher;
117:            /**
118:             * Stores the variable suffix.
119:             */
120:            private StrMatcher suffixMatcher;
121:            /**
122:             * Variable resolution is delegated to an implementor of VariableResolver.
123:             */
124:            private StrLookup variableResolver;
125:
126:            //-----------------------------------------------------------------------
127:            /**
128:             * Replaces all the occurrences of variables in the given source object with
129:             * their matching values from the map.
130:             *
131:             * @param source  the source text containing the variables to substitute, null returns null
132:             * @param valueMap  the map with the values, may be null
133:             * @return the result of the replace operation
134:             */
135:            public static String replace(Object source, Map valueMap) {
136:                return new StrSubstitutor(valueMap).replace(source);
137:            }
138:
139:            /**
140:             * Replaces all the occurrences of variables in the given source object with
141:             * their matching values from the map. This method allows to specifiy a
142:             * custom variable prefix and suffix
143:             *
144:             * @param source  the source text containing the variables to substitute, null returns null
145:             * @param valueMap  the map with the values, may be null
146:             * @param prefix  the prefix of variables, not null
147:             * @param suffix  the suffix of variables, not null
148:             * @return the result of the replace operation
149:             * @throws IllegalArgumentException if the prefix or suffix is null
150:             */
151:            public static String replace(Object source, Map valueMap,
152:                    String prefix, String suffix) {
153:                return new StrSubstitutor(valueMap, prefix, suffix)
154:                        .replace(source);
155:            }
156:
157:            /**
158:             * Replaces all the occurrences of variables in the given source object with
159:             * their matching values from the system properties.
160:             *
161:             * @param source  the source text containing the variables to substitute, null returns null
162:             * @return the result of the replace operation
163:             */
164:            public static String replaceSystemProperties(Object source) {
165:                return new StrSubstitutor(StrLookup.systemPropertiesLookup())
166:                        .replace(source);
167:            }
168:
169:            //-----------------------------------------------------------------------
170:            /**
171:             * Creates a new instance with defaults for variable prefix and suffix
172:             * and the escaping character.
173:             */
174:            public StrSubstitutor() {
175:                this ((StrLookup) null, DEFAULT_PREFIX, DEFAULT_SUFFIX,
176:                        DEFAULT_ESCAPE);
177:            }
178:
179:            /**
180:             * Creates a new instance and initializes it. Uses defaults for variable
181:             * prefix and suffix and the escaping character.
182:             *
183:             * @param valueMap  the map with the variables' values, may be null
184:             */
185:            public StrSubstitutor(Map valueMap) {
186:                this (StrLookup.mapLookup(valueMap), DEFAULT_PREFIX,
187:                        DEFAULT_SUFFIX, DEFAULT_ESCAPE);
188:            }
189:
190:            /**
191:             * Creates a new instance and initializes it. Uses a default escaping character.
192:             *
193:             * @param valueMap  the map with the variables' values, may be null
194:             * @param prefix  the prefix for variables, not null
195:             * @param suffix  the suffix for variables, not null
196:             * @throws IllegalArgumentException if the prefix or suffix is null
197:             */
198:            public StrSubstitutor(Map valueMap, String prefix, String suffix) {
199:                this (StrLookup.mapLookup(valueMap), prefix, suffix,
200:                        DEFAULT_ESCAPE);
201:            }
202:
203:            /**
204:             * Creates a new instance and initializes it.
205:             *
206:             * @param valueMap  the map with the variables' values, may be null
207:             * @param prefix  the prefix for variables, not null
208:             * @param suffix  the suffix for variables, not null
209:             * @param escape  the escape character
210:             * @throws IllegalArgumentException if the prefix or suffix is null
211:             */
212:            public StrSubstitutor(Map valueMap, String prefix, String suffix,
213:                    char escape) {
214:                this (StrLookup.mapLookup(valueMap), prefix, suffix, escape);
215:            }
216:
217:            /**
218:             * Creates a new instance and initializes it.
219:             *
220:             * @param variableResolver  the variable resolver, may be null
221:             */
222:            public StrSubstitutor(StrLookup variableResolver) {
223:                this (variableResolver, DEFAULT_PREFIX, DEFAULT_SUFFIX,
224:                        DEFAULT_ESCAPE);
225:            }
226:
227:            /**
228:             * Creates a new instance and initializes it.
229:             *
230:             * @param variableResolver  the variable resolver, may be null
231:             * @param prefix  the prefix for variables, not null
232:             * @param suffix  the suffix for variables, not null
233:             * @param escape  the escape character
234:             * @throws IllegalArgumentException if the prefix or suffix is null
235:             */
236:            public StrSubstitutor(StrLookup variableResolver, String prefix,
237:                    String suffix, char escape) {
238:                this .setVariableResolver(variableResolver);
239:                this .setVariablePrefix(prefix);
240:                this .setVariableSuffix(suffix);
241:                this .setEscapeChar(escape);
242:            }
243:
244:            /**
245:             * Creates a new instance and initializes it.
246:             *
247:             * @param variableResolver  the variable resolver, may be null
248:             * @param prefixMatcher  the prefix for variables, not null
249:             * @param suffixMatcher  the suffix for variables, not null
250:             * @param escape  the escape character
251:             * @throws IllegalArgumentException if the prefix or suffix is null
252:             */
253:            public StrSubstitutor(StrLookup variableResolver,
254:                    StrMatcher prefixMatcher, StrMatcher suffixMatcher,
255:                    char escape) {
256:                this .setVariableResolver(variableResolver);
257:                this .setVariablePrefixMatcher(prefixMatcher);
258:                this .setVariableSuffixMatcher(suffixMatcher);
259:                this .setEscapeChar(escape);
260:            }
261:
262:            //-----------------------------------------------------------------------
263:            /**
264:             * Replaces all the occurrences of variables with their matching values
265:             * from the resolver using the given source string as a template.
266:             *
267:             * @param source  the string to replace in, null returns null
268:             * @return the result of the replace operation
269:             */
270:            public String replace(String source) {
271:                if (source == null) {
272:                    return null;
273:                }
274:                StrBuilder buf = new StrBuilder(source);
275:                if (substitute(buf, 0, source.length()) == false) {
276:                    return source;
277:                }
278:                return buf.toString();
279:            }
280:
281:            /**
282:             * Replaces all the occurrences of variables with their matching values
283:             * from the resolver using the given source string as a template.
284:             * <p>
285:             * Only the specified portion of the string will be processed.
286:             * The rest of the string is not processed, and is not returned.
287:             *
288:             * @param source  the string to replace in, null returns null
289:             * @param offset  the start offset within the array, must be valid
290:             * @param length  the length within the array to be processed, must be valid
291:             * @return the result of the replace operation
292:             */
293:            public String replace(String source, int offset, int length) {
294:                if (source == null) {
295:                    return null;
296:                }
297:                StrBuilder buf = new StrBuilder(length).append(source, offset,
298:                        length);
299:                if (substitute(buf, 0, length) == false) {
300:                    return source.substring(offset, offset + length);
301:                }
302:                return buf.toString();
303:            }
304:
305:            //-----------------------------------------------------------------------
306:            /**
307:             * Replaces all the occurrences of variables with their matching values
308:             * from the resolver using the given source array as a template.
309:             * The array is not altered by this method.
310:             *
311:             * @param source  the character array to replace in, not altered, null returns null
312:             * @return the result of the replace operation
313:             */
314:            public String replace(char[] source) {
315:                if (source == null) {
316:                    return null;
317:                }
318:                StrBuilder buf = new StrBuilder(source.length).append(source);
319:                substitute(buf, 0, source.length);
320:                return buf.toString();
321:            }
322:
323:            /**
324:             * Replaces all the occurrences of variables with their matching values
325:             * from the resolver using the given source array as a template.
326:             * The array is not altered by this method.
327:             * <p>
328:             * Only the specified portion of the array will be processed.
329:             * The rest of the array is not processed, and is not returned.
330:             *
331:             * @param source  the character array to replace in, not altered, null returns null
332:             * @param offset  the start offset within the array, must be valid
333:             * @param length  the length within the array to be processed, must be valid
334:             * @return the result of the replace operation
335:             */
336:            public String replace(char[] source, int offset, int length) {
337:                if (source == null) {
338:                    return null;
339:                }
340:                StrBuilder buf = new StrBuilder(length).append(source, offset,
341:                        length);
342:                substitute(buf, 0, length);
343:                return buf.toString();
344:            }
345:
346:            //-----------------------------------------------------------------------
347:            /**
348:             * Replaces all the occurrences of variables with their matching values
349:             * from the resolver using the given source buffer as a template.
350:             * The buffer is not altered by this method.
351:             *
352:             * @param source  the buffer to use as a template, not changed, null returns null
353:             * @return the result of the replace operation
354:             */
355:            public String replace(StringBuffer source) {
356:                if (source == null) {
357:                    return null;
358:                }
359:                StrBuilder buf = new StrBuilder(source.length()).append(source);
360:                substitute(buf, 0, buf.length());
361:                return buf.toString();
362:            }
363:
364:            /**
365:             * Replaces all the occurrences of variables with their matching values
366:             * from the resolver using the given source buffer as a template.
367:             * The buffer is not altered by this method.
368:             * <p>
369:             * Only the specified portion of the buffer will be processed.
370:             * The rest of the buffer is not processed, and is not returned.
371:             *
372:             * @param source  the buffer to use as a template, not changed, null returns null
373:             * @param offset  the start offset within the array, must be valid
374:             * @param length  the length within the array to be processed, must be valid
375:             * @return the result of the replace operation
376:             */
377:            public String replace(StringBuffer source, int offset, int length) {
378:                if (source == null) {
379:                    return null;
380:                }
381:                StrBuilder buf = new StrBuilder(length).append(source, offset,
382:                        length);
383:                substitute(buf, 0, length);
384:                return buf.toString();
385:            }
386:
387:            //-----------------------------------------------------------------------
388:            /**
389:             * Replaces all the occurrences of variables with their matching values
390:             * from the resolver using the given source builder as a template.
391:             * The builder is not altered by this method.
392:             *
393:             * @param source  the builder to use as a template, not changed, null returns null
394:             * @return the result of the replace operation
395:             */
396:            public String replace(StrBuilder source) {
397:                if (source == null) {
398:                    return null;
399:                }
400:                StrBuilder buf = new StrBuilder(source.length()).append(source);
401:                substitute(buf, 0, buf.length());
402:                return buf.toString();
403:            }
404:
405:            /**
406:             * Replaces all the occurrences of variables with their matching values
407:             * from the resolver using the given source builder as a template.
408:             * The builder is not altered by this method.
409:             * <p>
410:             * Only the specified portion of the builder will be processed.
411:             * The rest of the builder is not processed, and is not returned.
412:             *
413:             * @param source  the builder to use as a template, not changed, null returns null
414:             * @param offset  the start offset within the array, must be valid
415:             * @param length  the length within the array to be processed, must be valid
416:             * @return the result of the replace operation
417:             */
418:            public String replace(StrBuilder source, int offset, int length) {
419:                if (source == null) {
420:                    return null;
421:                }
422:                StrBuilder buf = new StrBuilder(length).append(source, offset,
423:                        length);
424:                substitute(buf, 0, length);
425:                return buf.toString();
426:            }
427:
428:            //-----------------------------------------------------------------------
429:            /**
430:             * Replaces all the occurrences of variables in the given source object with
431:             * their matching values from the resolver. The input source object is
432:             * converted to a string using <code>toString</code> and is not altered.
433:             *
434:             * @param source  the source to replace in, null returns null
435:             * @return the result of the replace operation
436:             */
437:            public String replace(Object source) {
438:                if (source == null) {
439:                    return null;
440:                }
441:                StrBuilder buf = new StrBuilder().append(source);
442:                substitute(buf, 0, buf.length());
443:                return buf.toString();
444:            }
445:
446:            //-----------------------------------------------------------------------
447:            /**
448:             * Replaces all the occurrences of variables within the given source buffer
449:             * with their matching values from the resolver.
450:             * The buffer is updated with the result.
451:             *
452:             * @param source  the buffer to replace in, updated, null returns zero
453:             * @return true if altered
454:             */
455:            public boolean replaceIn(StringBuffer source) {
456:                if (source == null) {
457:                    return false;
458:                }
459:                return replaceIn(source, 0, source.length());
460:            }
461:
462:            /**
463:             * Replaces all the occurrences of variables within the given source buffer
464:             * with their matching values from the resolver.
465:             * The buffer is updated with the result.
466:             * <p>
467:             * Only the specified portion of the buffer will be processed.
468:             * The rest of the buffer is not processed, but it is not deleted.
469:             *
470:             * @param source  the buffer to replace in, updated, null returns zero
471:             * @param offset  the start offset within the array, must be valid
472:             * @param length  the length within the buffer to be processed, must be valid
473:             * @return true if altered
474:             */
475:            public boolean replaceIn(StringBuffer source, int offset, int length) {
476:                if (source == null) {
477:                    return false;
478:                }
479:                StrBuilder buf = new StrBuilder(length).append(source, offset,
480:                        length);
481:                if (substitute(buf, 0, length) == false) {
482:                    return false;
483:                }
484:                source.replace(offset, offset + length, buf.toString());
485:                return true;
486:            }
487:
488:            //-----------------------------------------------------------------------
489:            /**
490:             * Replaces all the occurrences of variables within the given source
491:             * builder with their matching values from the resolver.
492:             *
493:             * @param source  the builder to replace in, updated, null returns zero
494:             * @return true if altered
495:             */
496:            public boolean replaceIn(StrBuilder source) {
497:                if (source == null) {
498:                    return false;
499:                }
500:                return substitute(source, 0, source.length());
501:            }
502:
503:            /**
504:             * Replaces all the occurrences of variables within the given source
505:             * builder with their matching values from the resolver.
506:             * <p>
507:             * Only the specified portion of the builder will be processed.
508:             * The rest of the builder is not processed, but it is not deleted.
509:             *
510:             * @param source  the builder to replace in, null returns zero
511:             * @param offset  the start offset within the array, must be valid
512:             * @param length  the length within the builder to be processed, must be valid
513:             * @return true if altered
514:             */
515:            public boolean replaceIn(StrBuilder source, int offset, int length) {
516:                if (source == null) {
517:                    return false;
518:                }
519:                return substitute(source, offset, length);
520:            }
521:
522:            //-----------------------------------------------------------------------
523:            /**
524:             * Internal method that substitutes the variables.
525:             * <p>
526:             * Most users of this class do not need to call this method. This method will
527:             * be called automatically by another (public) method.
528:             * <p>
529:             * Writers of subclasses can override this method if they need access to
530:             * the substitution process at the start or end.
531:             *
532:             * @param buf  the string builder to substitute into, not null
533:             * @param offset  the start offset within the builder, must be valid
534:             * @param length  the length within the builder to be processed, must be valid
535:             * @return true if altered
536:             */
537:            protected boolean substitute(StrBuilder buf, int offset, int length) {
538:                return substitute(buf, offset, length, null) > 0;
539:            }
540:
541:            /**
542:             * Recursive handler for multiple levels of interpolation. This is the main
543:             * interpolation method, which resolves the values of all variable references
544:             * contained in the passed in text.
545:             *
546:             * @param buf  the string builder to substitute into, not null
547:             * @param offset  the start offset within the builder, must be valid
548:             * @param length  the length within the builder to be processed, must be valid
549:             * @param priorVariables  the stack keeping track of the replaced variables, may be null
550:             * @return the length change that occurs, unless priorVariables is null when the int
551:             *  represents a boolean flag as to whether any change occurred.
552:             */
553:            private int substitute(StrBuilder buf, int offset, int length,
554:                    List priorVariables) {
555:                StrMatcher prefixMatcher = getVariablePrefixMatcher();
556:                StrMatcher suffixMatcher = getVariableSuffixMatcher();
557:                char escape = getEscapeChar();
558:
559:                boolean top = (priorVariables == null);
560:                boolean altered = false;
561:                int lengthChange = 0;
562:                char[] chars = buf.buffer;
563:                int bufEnd = offset + length;
564:                int pos = offset;
565:                while (pos < bufEnd) {
566:                    int startMatchLen = prefixMatcher.isMatch(chars, pos,
567:                            offset, bufEnd);
568:                    if (startMatchLen == 0) {
569:                        pos++;
570:                    } else {
571:                        // found variable start marker
572:                        if (pos > offset && chars[pos - 1] == escape) {
573:                            // escaped
574:                            buf.deleteCharAt(pos - 1);
575:                            chars = buf.buffer; // in case buffer was altered
576:                            lengthChange--;
577:                            altered = true;
578:                            bufEnd--;
579:                        } else {
580:                            // find suffix
581:                            int startPos = pos;
582:                            pos += startMatchLen;
583:                            int endMatchLen = 0;
584:                            while (pos < bufEnd) {
585:                                endMatchLen = suffixMatcher.isMatch(chars, pos,
586:                                        offset, bufEnd);
587:                                if (endMatchLen == 0) {
588:                                    pos++;
589:                                } else {
590:                                    // found variable end marker
591:                                    String varName = new String(chars, startPos
592:                                            + startMatchLen, pos - startPos
593:                                            - startMatchLen);
594:                                    pos += endMatchLen;
595:                                    int endPos = pos;
596:
597:                                    // on the first call initialize priorVariables
598:                                    if (priorVariables == null) {
599:                                        priorVariables = new ArrayList();
600:                                        priorVariables.add(new String(chars,
601:                                                offset, length));
602:                                    }
603:
604:                                    // handle cyclic substitution
605:                                    checkCyclicSubstitution(varName,
606:                                            priorVariables);
607:                                    priorVariables.add(varName);
608:
609:                                    // resolve the variable
610:                                    String varValue = resolveVariable(varName,
611:                                            buf, startPos, endPos);
612:                                    if (varValue != null) {
613:                                        // recursive replace
614:                                        int varLen = varValue.length();
615:                                        buf.replace(startPos, endPos, varValue);
616:                                        altered = true;
617:                                        int change = substitute(buf, startPos,
618:                                                varLen, priorVariables);
619:                                        change = change
620:                                                + (varLen - (endPos - startPos));
621:                                        pos += change;
622:                                        bufEnd += change;
623:                                        lengthChange += change;
624:                                        chars = buf.buffer; // in case buffer was altered
625:                                    }
626:
627:                                    // remove variable from the cyclic stack
628:                                    priorVariables
629:                                            .remove(priorVariables.size() - 1);
630:                                    break;
631:                                }
632:                            }
633:                        }
634:                    }
635:                }
636:                if (top) {
637:                    return (altered ? 1 : 0);
638:                }
639:                return lengthChange;
640:            }
641:
642:            /**
643:             * Checks if the specified variable is already in the stack (list) of variables.
644:             *
645:             * @param varName  the variable name to check
646:             * @param priorVariables  the list of prior variables
647:             */
648:            private void checkCyclicSubstitution(String varName,
649:                    List priorVariables) {
650:                if (priorVariables.contains(varName) == false) {
651:                    return;
652:                }
653:                StrBuilder buf = new StrBuilder(256);
654:                buf.append("Infinite loop in property interpolation of ");
655:                buf.append(priorVariables.remove(0));
656:                buf.append(": ");
657:                buf.appendWithSeparators(priorVariables, "->");
658:                throw new IllegalStateException(buf.toString());
659:            }
660:
661:            /**
662:             * Internal method that resolves the value of a variable.
663:             * <p>
664:             * Most users of this class do not need to call this method. This method is
665:             * called automatically by the substitution process.
666:             * <p>
667:             * Writers of subclasses can override this method if they need to alter
668:             * how each substitution occurs. The method is passed the variable's name
669:             * and must return the corresponding value. This implementation uses the
670:             * {@link #getVariableResolver()} with the variable's name as the key.
671:             *
672:             * @param variableName  the name of the variable, not null
673:             * @param buf  the buffer where the substitution is occurring, not null
674:             * @param startPos  the start position of the variable including the prefix, valid
675:             * @param endPos  the end position of the variable including the suffix, valid
676:             * @return the variable's value or <b>null</b> if the variable is unknown
677:             */
678:            protected String resolveVariable(String variableName,
679:                    StrBuilder buf, int startPos, int endPos) {
680:                StrLookup resolver = getVariableResolver();
681:                if (resolver == null) {
682:                    return null;
683:                }
684:                return resolver.lookup(variableName);
685:            }
686:
687:            // Escape
688:            //-----------------------------------------------------------------------
689:            /**
690:             * Returns the escape character.
691:             *
692:             * @return the character used for escaping variable references
693:             */
694:            public char getEscapeChar() {
695:                return this .escapeChar;
696:            }
697:
698:            /**
699:             * Sets the escape character.
700:             * If this character is placed before a variable reference in the source
701:             * text, this variable will be ignored.
702:             *
703:             * @param escapeCharacter  the escape character (0 for disabling escaping)
704:             */
705:            public void setEscapeChar(char escapeCharacter) {
706:                this .escapeChar = escapeCharacter;
707:            }
708:
709:            // Prefix
710:            //-----------------------------------------------------------------------
711:            /**
712:             * Gets the variable prefix matcher currently in use.
713:             * <p>
714:             * The variable prefix is the characer or characters that identify the
715:             * start of a variable. This prefix is expressed in terms of a matcher
716:             * allowing advanced prefix matches.
717:             *
718:             * @return the prefix matcher in use
719:             */
720:            public StrMatcher getVariablePrefixMatcher() {
721:                return prefixMatcher;
722:            }
723:
724:            /**
725:             * Sets the variable prefix matcher currently in use.
726:             * <p>
727:             * The variable prefix is the characer or characters that identify the
728:             * start of a variable. This prefix is expressed in terms of a matcher
729:             * allowing advanced prefix matches.
730:             *
731:             * @param prefixMatcher  the prefix matcher to use, null ignored
732:             * @return this, to enable chaining
733:             * @throws IllegalArgumentException if the prefix matcher is null
734:             */
735:            public StrSubstitutor setVariablePrefixMatcher(
736:                    StrMatcher prefixMatcher) {
737:                if (prefixMatcher == null) {
738:                    throw new IllegalArgumentException(
739:                            "Variable prefix matcher must not be null!");
740:                }
741:                this .prefixMatcher = prefixMatcher;
742:                return this ;
743:            }
744:
745:            /**
746:             * Sets the variable prefix to use.
747:             * <p>
748:             * The variable prefix is the characer or characters that identify the
749:             * start of a variable. This method allows a single character prefix to
750:             * be easily set.
751:             *
752:             * @param prefix  the prefix character to use
753:             * @return this, to enable chaining
754:             */
755:            public StrSubstitutor setVariablePrefix(char prefix) {
756:                return setVariablePrefixMatcher(StrMatcher.charMatcher(prefix));
757:            }
758:
759:            /**
760:             * Sets the variable prefix to use.
761:             * <p>
762:             * The variable prefix is the characer or characters that identify the
763:             * start of a variable. This method allows a string prefix to be easily set.
764:             *
765:             * @param prefix  the prefix for variables, not null
766:             * @return this, to enable chaining
767:             * @throws IllegalArgumentException if the prefix is null
768:             */
769:            public StrSubstitutor setVariablePrefix(String prefix) {
770:                if (prefix == null) {
771:                    throw new IllegalArgumentException(
772:                            "Variable prefix must not be null!");
773:                }
774:                return setVariablePrefixMatcher(StrMatcher
775:                        .stringMatcher(prefix));
776:            }
777:
778:            // Suffix
779:            //-----------------------------------------------------------------------
780:            /**
781:             * Gets the variable suffix matcher currently in use.
782:             * <p>
783:             * The variable suffix is the characer or characters that identify the
784:             * end of a variable. This suffix is expressed in terms of a matcher
785:             * allowing advanced suffix matches.
786:             *
787:             * @return the suffix matcher in use
788:             */
789:            public StrMatcher getVariableSuffixMatcher() {
790:                return suffixMatcher;
791:            }
792:
793:            /**
794:             * Sets the variable suffix matcher currently in use.
795:             * <p>
796:             * The variable suffix is the characer or characters that identify the
797:             * end of a variable. This suffix is expressed in terms of a matcher
798:             * allowing advanced suffix matches.
799:             *
800:             * @param suffixMatcher  the suffix matcher to use, null ignored
801:             * @return this, to enable chaining
802:             * @throws IllegalArgumentException if the suffix matcher is null
803:             */
804:            public StrSubstitutor setVariableSuffixMatcher(
805:                    StrMatcher suffixMatcher) {
806:                if (suffixMatcher == null) {
807:                    throw new IllegalArgumentException(
808:                            "Variable suffix matcher must not be null!");
809:                }
810:                this .suffixMatcher = suffixMatcher;
811:                return this ;
812:            }
813:
814:            /**
815:             * Sets the variable suffix to use.
816:             * <p>
817:             * The variable suffix is the characer or characters that identify the
818:             * end of a variable. This method allows a single character suffix to
819:             * be easily set.
820:             *
821:             * @param suffix  the suffix character to use
822:             * @return this, to enable chaining
823:             */
824:            public StrSubstitutor setVariableSuffix(char suffix) {
825:                return setVariableSuffixMatcher(StrMatcher.charMatcher(suffix));
826:            }
827:
828:            /**
829:             * Sets the variable suffix to use.
830:             * <p>
831:             * The variable suffix is the characer or characters that identify the
832:             * end of a variable. This method allows a string suffix to be easily set.
833:             *
834:             * @param suffix  the suffix for variables, not null
835:             * @return this, to enable chaining
836:             * @throws IllegalArgumentException if the suffix is null
837:             */
838:            public StrSubstitutor setVariableSuffix(String suffix) {
839:                if (suffix == null) {
840:                    throw new IllegalArgumentException(
841:                            "Variable suffix must not be null!");
842:                }
843:                return setVariableSuffixMatcher(StrMatcher
844:                        .stringMatcher(suffix));
845:            }
846:
847:            // Resolver
848:            //-----------------------------------------------------------------------
849:            /**
850:             * Gets the VariableResolver that is used to lookup variables.
851:             *
852:             * @return the VariableResolver
853:             */
854:            public StrLookup getVariableResolver() {
855:                return this .variableResolver;
856:            }
857:
858:            /**
859:             * Sets the VariableResolver that is used to lookup variables.
860:             *
861:             * @param variableResolver  the VariableResolver
862:             */
863:            public void setVariableResolver(StrLookup variableResolver) {
864:                this.variableResolver = variableResolver;
865:            }
866:
867:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.