View | Details | Raw Unified | Return to bug 40184
Collapse All | Expand All

(-)src/main/org/apache/tools/ant/PropertyHelper.java (+228 lines)
Lines 18-23 Link Here
18
package org.apache.tools.ant;
18
package org.apache.tools.ant;
19
19
20
import java.util.Hashtable;
20
import java.util.Hashtable;
21
import java.util.Locale;
22
import java.util.Map;
23
import java.util.Iterator;
24
import java.util.Stack;
21
import java.util.Vector;
25
import java.util.Vector;
22
import java.util.Enumeration;
26
import java.util.Enumeration;
23
27
Lines 287-292 Link Here
287
        return sb.toString();
291
        return sb.toString();
288
    }
292
    }
289
293
294
    private static final int STATE_NORMAL         = 0;
295
    private static final int STATE_EXPECT_BRACKET = 1;
296
    private static final int STATE_EXPECT_NAME    = 2;
297
298
    /**
299
     * Replaces <code>@{xxx}</code> style constructions in the given value
300
     * with the string value of the corresponding data types.
301
     * 
302
     * @param s The string to be scanned for attribute references.
303
     *          May be <code>null</code>, in which case this
304
     *          method returns immediately with no effect.
305
     * @param attrMapping Mapping (String to String) of attribute names 
306
     *                    to their values.
307
     * @return the original string with the attributes replaced, or
308
     *         <code>null</code> if the original string is <code>null</code>.
309
     */
310
    public synchronized String replaceAttributes(String s, Map attrMapping) {
311
        if (s == null) {
312
            return null;
313
        }
314
        StringBuffer ret = new StringBuffer();
315
        StringBuffer macroName = null;
316
317
        int state = STATE_NORMAL;
318
        for (int i = 0; i < s.length(); ++i) {
319
            char ch = s.charAt(i);
320
            switch (state) {
321
                case STATE_NORMAL:
322
                    if (ch == '@') {
323
                        state = STATE_EXPECT_BRACKET;
324
                    } else {
325
                        ret.append(ch);
326
                    }
327
                    break;
328
                case STATE_EXPECT_BRACKET:
329
                    if (ch == '{') {
330
                        state = STATE_EXPECT_NAME;
331
                        macroName = new StringBuffer();
332
                    } else if (ch == '@') {
333
                        state = STATE_NORMAL;
334
                        ret.append('@');
335
                    } else {
336
                        state = STATE_NORMAL;
337
                        ret.append('@');
338
                        ret.append(ch);
339
                    }
340
                    break;
341
                case STATE_EXPECT_NAME:
342
                    if (ch == '}') {
343
                        state = STATE_NORMAL;
344
                        String name = macroName.toString().toLowerCase(Locale.US);
345
                        String value = (String) attrMapping.get(name);
346
                        if (value == null) {
347
                            ret.append("@{");
348
                            ret.append(name);
349
                            ret.append("}");
350
                        } else {
351
                            ret.append(value);
352
                        }
353
                        macroName = null;
354
                    } else {
355
                        macroName.append(ch);
356
                    }
357
                    break;
358
                default:
359
                    break;
360
            }
361
        }
362
        switch (state) {
363
            case STATE_NORMAL:
364
                break;
365
            case STATE_EXPECT_BRACKET:
366
                ret.append('@');
367
                break;
368
            case STATE_EXPECT_NAME:
369
                ret.append("@{");
370
                ret.append(macroName.toString());
371
                break;
372
            default:
373
                break;
374
        }
375
376
        return ret.toString();
377
    }
378
290
    // -------------------- Default implementation  --------------------
379
    // -------------------- Default implementation  --------------------
291
    // Methods used to support the default behavior and provide backward
380
    // Methods used to support the default behavior and provide backward
292
    // compatibility. Some will be deprecated, you should avoid calling them.
381
    // compatibility. Some will be deprecated, you should avoid calling them.
Lines 411-416 Link Here
411
        properties.put(name, value);
500
        properties.put(name, value);
412
    }
501
    }
413
502
503
    /**
504
     * Set a new inherited user property, which cannot be overwritten 
505
     * by set/unset property calls. If a user property with the
506
     * same name is already present the value is not overridden.
507
     * 
508
     * @param ns The namespace for the property (currently not used).
509
     * @param n Name of property
510
     * @param v Value to set
511
     */
512
    public synchronized void setNewInheritedProperty(String ns, 
513
            String n, String v) {
514
        if (getUserProperty(null, n) == null) {
515
            setInheritedProperty(null, n, v);
516
        } else {
517
            project.log("Override ignored for " + n, Project.MSG_VERBOSE);
518
        }
519
    }
520
521
    /**
522
     * Adds a set of unexpanded properties.
523
     * <p>The properties are resolved, prefixed, expanded and added with 
524
     * {@link #setNewProperty(String, String, Object)}.</p>
525
     * @param props The unexpanded properties.
526
     * @param prefix The prefix to add to each property name
527
     *               after properties resolved.
528
     *               <code>null</code> if no prefix.
529
     */
530
    public synchronized void addNewProperties(String ns
531
            , Map props, String prefix) {
532
        addUnexpandedProperties(ns, props, prefix, false);
533
    }
534
535
    /**
536
     * Adds a set of unexpanded inherited user properties.
537
     * <p>The properties are resolved, prefixed, expanded and added with 
538
     * {@link #setNewInheritedProperty(String, String, String)}.</p>
539
     * @param props The unexpanded inherited user properties.
540
     * @param prefix The prefix to add to each property name
541
     *               after properties resolved.
542
     *               <code>null</code> if no prefix.
543
     */
544
    public synchronized void addNewInheritedProperties(String ns
545
            , Map props, String prefix) {
546
        addUnexpandedProperties(ns, props, prefix, true);
547
    }
548
549
    /**
550
     * Iterate through a map of properties,
551
     * resolve them and assign them.
552
     * @param props The properties to iterate over.
553
     * @param prefix The prefix to add to each name.
554
     * @param userProperties Whether to be added as user properties.
555
     */
556
    private void addUnexpandedProperties(String ns
557
            , Map props, String prefix, boolean userProperties) {
558
        resolveAllProperties(props);
559
        Iterator it = props.entrySet().iterator();
560
        while (it.hasNext()) {
561
            Map.Entry entry = (Map.Entry)it.next();
562
            String propertyName = (String) entry.getKey();
563
            String propertyValue = (String) entry.getValue();
564
565
            String v = replaceProperties(null, propertyValue, null);
566
567
            if (prefix != null) {
568
                propertyName = prefix + propertyName;
569
            }
570
571
            if (userProperties) {
572
                setNewInheritedProperty(null, propertyName, v);
573
            } else {
574
                setNewProperty(null, propertyName, v);
575
            }
576
        }
577
    }
578
579
    /**
580
     * Resolve properties inside a properties map.
581
     * @param props Map of properties to resolve.
582
     */
583
    private void resolveAllProperties(Map props) throws BuildException {
584
        Iterator it = props.entrySet().iterator();
585
        while (it.hasNext()) {
586
            Map.Entry entry = (Map.Entry)it.next();
587
            String propertyName = (String) entry.getKey();
588
            Stack referencesSeen = new Stack();
589
            resolve(props, propertyName, referencesSeen);
590
        }
591
    }
592
593
    /**
594
     * Recursively expand the named property using the given 
595
     * properties map - fail if a circular definition is detected.
596
     *
597
     * @param props Map of properties to resolve
598
     * @param name Name of the property to resolve
599
     * @param referencesSeen Stack of all property names that have
600
     *                       been tried to expand before coming here.
601
     */
602
    private void resolve(Map props, String name, Stack referencesSeen)
603
        throws BuildException {
604
        if (referencesSeen.contains(name)) {
605
            throw new BuildException("Property " + name + " was circularly "
606
                                     + "defined.");
607
        }
608
609
        String propertyValue = (String)props.get(name);
610
        Vector fragments = new Vector();
611
        Vector propertyRefs = new Vector();
612
        parsePropertyString(propertyValue, fragments, propertyRefs);
613
614
        if (propertyRefs.size() != 0) {
615
            referencesSeen.push(name);
616
            StringBuffer sb = new StringBuffer();
617
            Enumeration i = fragments.elements();
618
            Enumeration j = propertyRefs.elements();
619
            while (i.hasMoreElements()) {
620
                String fragment = (String) i.nextElement();
621
                if (fragment == null) {
622
                    String propertyName = (String) j.nextElement();
623
                    Object o = getProperty(null, propertyName);
624
                    fragment = (o == null) ? null : o.toString();
625
                    if (fragment == null) {
626
                        if (props.containsKey(propertyName)) {
627
                            resolve(props, propertyName, referencesSeen);
628
                            fragment = (String)props.get(propertyName);
629
                        } else {
630
                            fragment = "${" + propertyName + "}";
631
                        }
632
                    }
633
                }
634
                sb.append(fragment);
635
            }
636
            propertyValue = sb.toString();
637
            props.put(name, propertyValue);
638
            referencesSeen.pop();
639
        }
640
    }
641
414
    // -------------------- Getting properties  --------------------
642
    // -------------------- Getting properties  --------------------
415
643
416
    /**
644
    /**
(-)src/main/org/apache/tools/ant/taskdefs/MacroInstance.java (-76 / +8 lines)
Lines 31-36 Link Here
31
import org.apache.tools.ant.BuildException;
31
import org.apache.tools.ant.BuildException;
32
import org.apache.tools.ant.DynamicAttribute;
32
import org.apache.tools.ant.DynamicAttribute;
33
import org.apache.tools.ant.ProjectHelper;
33
import org.apache.tools.ant.ProjectHelper;
34
import org.apache.tools.ant.PropertyHelper;
34
import org.apache.tools.ant.RuntimeConfigurable;
35
import org.apache.tools.ant.RuntimeConfigurable;
35
import org.apache.tools.ant.Target;
36
import org.apache.tools.ant.Target;
36
import org.apache.tools.ant.Task;
37
import org.apache.tools.ant.Task;
Lines 159-237 Link Here
159
        }
160
        }
160
    }
161
    }
161
162
162
    private static final int STATE_NORMAL         = 0;
163
    private static final int STATE_EXPECT_BRACKET = 1;
164
    private static final int STATE_EXPECT_NAME    = 2;
165
166
    private String macroSubs(String s, Map macroMapping) {
167
        if (s == null) {
168
            return null;
169
        }
170
        StringBuffer ret = new StringBuffer();
171
        StringBuffer macroName = null;
172
173
        int state = STATE_NORMAL;
174
        for (int i = 0; i < s.length(); ++i) {
175
            char ch = s.charAt(i);
176
            switch (state) {
177
                case STATE_NORMAL:
178
                    if (ch == '@') {
179
                        state = STATE_EXPECT_BRACKET;
180
                    } else {
181
                        ret.append(ch);
182
                    }
183
                    break;
184
                case STATE_EXPECT_BRACKET:
185
                    if (ch == '{') {
186
                        state = STATE_EXPECT_NAME;
187
                        macroName = new StringBuffer();
188
                    } else if (ch == '@') {
189
                        state = STATE_NORMAL;
190
                        ret.append('@');
191
                    } else {
192
                        state = STATE_NORMAL;
193
                        ret.append('@');
194
                        ret.append(ch);
195
                    }
196
                    break;
197
                case STATE_EXPECT_NAME:
198
                    if (ch == '}') {
199
                        state = STATE_NORMAL;
200
                        String name = macroName.toString().toLowerCase(Locale.US);
201
                        String value = (String) macroMapping.get(name);
202
                        if (value == null) {
203
                            ret.append("@{");
204
                            ret.append(name);
205
                            ret.append("}");
206
                        } else {
207
                            ret.append(value);
208
                        }
209
                        macroName = null;
210
                    } else {
211
                        macroName.append(ch);
212
                    }
213
                    break;
214
                default:
215
                    break;
216
            }
217
        }
218
        switch (state) {
219
            case STATE_NORMAL:
220
                break;
221
            case STATE_EXPECT_BRACKET:
222
                ret.append('@');
223
                break;
224
            case STATE_EXPECT_NAME:
225
                ret.append("@{");
226
                ret.append(macroName.toString());
227
                break;
228
            default:
229
                break;
230
        }
231
232
        return ret.toString();
233
    }
234
235
    /**
163
    /**
236
     * Set the text contents for the macro.
164
     * Set the text contents for the macro.
237
     * @param text the text to be added to the macro.
165
     * @param text the text to be added to the macro.
Lines 259-272 Link Here
259
        RuntimeConfigurable rc = new RuntimeConfigurable(
187
        RuntimeConfigurable rc = new RuntimeConfigurable(
260
            ret, ue.getTaskName());
188
            ret, ue.getTaskName());
261
        rc.setPolyType(ue.getWrapper().getPolyType());
189
        rc.setPolyType(ue.getWrapper().getPolyType());
190
        PropertyHelper ph = PropertyHelper.getPropertyHelper(getProject());
262
        Map m = ue.getWrapper().getAttributeMap();
191
        Map m = ue.getWrapper().getAttributeMap();
263
        for (Iterator i = m.entrySet().iterator(); i.hasNext();) {
192
        for (Iterator i = m.entrySet().iterator(); i.hasNext();) {
264
            Map.Entry entry = (Map.Entry) i.next();
193
            Map.Entry entry = (Map.Entry) i.next();
265
            rc.setAttribute(
194
            rc.setAttribute(
266
                (String) entry.getKey(),
195
                (String) entry.getKey(),
267
                macroSubs((String) entry.getValue(), localAttributes));
196
                ph.replaceAttributes((String) entry.getValue(), localAttributes));
268
        }
197
        }
269
        rc.addText(macroSubs(ue.getWrapper().getText().toString(),
198
        rc.addText(ph.replaceAttributes(ue.getWrapper().getText().toString(),
270
                             localAttributes));
199
                             localAttributes));
271
200
272
        Enumeration e = ue.getWrapper().getChildren();
201
        Enumeration e = ue.getWrapper().getChildren();
Lines 323-328 Link Here
323
     *
252
     *
324
     */
253
     */
325
    public void execute() {
254
    public void execute() {
255
        PropertyHelper ph = PropertyHelper.getPropertyHelper(getProject());
256
        synchronized (ph) {
326
        presentElements = new HashMap();
257
        presentElements = new HashMap();
327
        getNsElements();
258
        getNsElements();
328
        processTasks();
259
        processTasks();
Lines 336-342 Link Here
336
            }
267
            }
337
            if (value == null) {
268
            if (value == null) {
338
                value = attribute.getDefault();
269
                value = attribute.getDefault();
339
                value = macroSubs(value, localAttributes);
270
                value = ph.replaceAttributes(value, localAttributes);
340
            } else if (attribute instanceof MacroDef.DefineAttribute) {
271
            } else if (attribute instanceof MacroDef.DefineAttribute) {
341
                // Do not process given value, will fail as unknown attribute
272
                // Do not process given value, will fail as unknown attribute
342
                continue;
273
                continue;
Lines 394-397 Link Here
394
            localAttributes = null;
325
            localAttributes = null;
395
        }
326
        }
396
    }
327
    }
328
    }
397
}
329
}
(-)src/main/org/apache/tools/ant/taskdefs/Property.java (-85 / +12 lines)
Lines 24-30 Link Here
24
import java.net.URL;
24
import java.net.URL;
25
import java.util.Enumeration;
25
import java.util.Enumeration;
26
import java.util.Properties;
26
import java.util.Properties;
27
import java.util.Stack;
28
import java.util.Vector;
27
import java.util.Vector;
29
28
30
import org.apache.tools.ant.BuildException;
29
import org.apache.tools.ant.BuildException;
Lines 535-637 Link Here
535
    }
534
    }
536
535
537
    /**
536
    /**
538
     * iterate through a set of properties,
537
     * Add the properties to the property helper 
539
     * resolve them then assign them
538
     * @param props the properties to add
540
     * @param props the properties to iterate over
541
     */
539
     */
542
    protected void addProperties(Properties props) {
540
    protected void addProperties(Properties props) {
543
        resolveAllProperties(props);
541
        PropertyHelper ph = PropertyHelper.getPropertyHelper(getProject());
544
        Enumeration e = props.keys();
542
        if (userProperty) {
545
        while (e.hasMoreElements()) {
543
            ph.addNewInheritedProperties(null, props, prefix);
546
            String propertyName = (String) e.nextElement();
544
        } else {
547
            String propertyValue = props.getProperty(propertyName);
545
            ph.addNewProperties(null, props, prefix);
548
549
            String v = getProject().replaceProperties(propertyValue);
550
551
            if (prefix != null) {
552
                propertyName = prefix + propertyName;
553
            }
554
555
            addProperty(propertyName, v);
556
        }
546
        }
557
    }
547
    }
558
548
559
    /**
549
    /**
560
     * add a name value pair to the project property set
550
     * Add a name value pair to the property helper
551
     * 
561
     * @param n name of property
552
     * @param n name of property
562
     * @param v value to set
553
     * @param v value to set
563
     */
554
     */
564
    protected void addProperty(String n, String v) {
555
    protected void addProperty(String n, String v) {
556
        PropertyHelper ph = PropertyHelper.getPropertyHelper(getProject());
565
        if (userProperty) {
557
        if (userProperty) {
566
            if (getProject().getUserProperty(n) == null) {
558
            ph.setNewInheritedProperty(null, n, v);
567
                getProject().setInheritedProperty(n, v);
568
            } else {
569
                log("Override ignored for " + n, Project.MSG_VERBOSE);
570
            }
571
        } else {
559
        } else {
572
            getProject().setNewProperty(n, v);
560
            ph.setNewProperty(null, n, v);
573
        }
561
        }
574
    }
562
    }
575
563
576
    /**
577
     * resolve properties inside a properties hashtable
578
     * @param props properties object to resolve
579
     */
580
    private void resolveAllProperties(Properties props) throws BuildException {
581
        for (Enumeration e = props.keys(); e.hasMoreElements();) {
582
            String propertyName = (String) e.nextElement();
583
            Stack referencesSeen = new Stack();
584
            resolve(props, propertyName, referencesSeen);
585
        }
586
    }
587
588
    /**
589
     * Recursively expand the named property using the project's
590
     * reference table and the given set of properties - fail if a
591
     * circular definition is detected.
592
     *
593
     * @param props properties object to resolve
594
     * @param name of the property to resolve
595
     * @param referencesSeen stack of all property names that have
596
     * been tried to expand before coming here.
597
     */
598
    private void resolve(Properties props, String name, Stack referencesSeen)
599
        throws BuildException {
600
        if (referencesSeen.contains(name)) {
601
            throw new BuildException("Property " + name + " was circularly "
602
                                     + "defined.");
603
        }
604
605
        String propertyValue = props.getProperty(name);
606
        Vector fragments = new Vector();
607
        Vector propertyRefs = new Vector();
608
        PropertyHelper.getPropertyHelper(this.getProject()).parsePropertyString(propertyValue, fragments,
609
                propertyRefs);
610
611
        if (propertyRefs.size() != 0) {
612
            referencesSeen.push(name);
613
            StringBuffer sb = new StringBuffer();
614
            Enumeration i = fragments.elements();
615
            Enumeration j = propertyRefs.elements();
616
            while (i.hasMoreElements()) {
617
                String fragment = (String) i.nextElement();
618
                if (fragment == null) {
619
                    String propertyName = (String) j.nextElement();
620
                    fragment = getProject().getProperty(propertyName);
621
                    if (fragment == null) {
622
                        if (props.containsKey(propertyName)) {
623
                            resolve(props, propertyName, referencesSeen);
624
                            fragment = props.getProperty(propertyName);
625
                        } else {
626
                            fragment = "${" + propertyName + "}";
627
                        }
628
                    }
629
                }
630
                sb.append(fragment);
631
            }
632
            propertyValue = sb.toString();
633
            props.put(name, propertyValue);
634
            referencesSeen.pop();
635
        }
636
    }
637
}
564
}

Return to bug 40184