ant-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Peter Reilly <peter.rei...@corvil.com>
Subject Re: <macro> and XML NS hell ;-)
Date Mon, 08 Mar 2004 18:04:25 GMT
Hi Gus,
I may have made a mistake using the addFileSet() method as the
resultant introspected nested
element is easy to confuse with the fileset type.

When ant looks at a class for introspections it just looks
for patterns addX(Y) where addX is a public void method
and Y is a class that has a public no-arg constructor, or a
constructor with a single Project arg (addConstructedX(Y) and Y createX()
methods are also considered).
So: if ant sees a class as follows

public class BadTask extends Task {
    public void addInclude(MyInclude d) {
    }
    public void addInclude(MyInclude2 d) {
    }
    public static class MyInclude {
        public void setName(String name) {}
    }
    public static class MyInclude2 {
        public void setFile(File file) {
        }
    }
}
it introspects that BadTask supports one nested element with the
"include" tag. It matches this nested element to the second
addInclude() method.
It does not do matching of the types for this form of
introspection - when the "include" tag is seen,  a MyInclude2 object
is created and it may have one attribute - file:

<project>
  <taskdef name="badtask" classname="task.BadTask"
           classpath="classes"/>
  <badtask>
    <include file="x"/>
  </badtask>
</project>

Now one may have a GoodTask type as follows:
public class GoodTask extends Task {
    public IncludeType createInclude() {
        return new IncludeType();
    }
    public class IncludeType {
        public void setFile(File file) {
        }
        public void setEnabled(boolean enabled) {
        }
    }
}

it could be called in this way:
  <goodtask>
     <include file="y"/>
   </goodtask>

and one can write a macrodef as follows:
<macrodef name="both">
   <element name="includes"/>
   <sequential>
      <badtask>
         <includes/>
       </badtask>
       <goodtask>
          <includes/>
        </goodtask>
    </sequential>
</macrodef>

and use it as follows:

 <both>
    <includes>
      <include file="n"/>
    </includes>
  </both>

The same macrodef will not work
if the goodtask and badtask are in
different namespaced antlibs.

Peter 


 Gus Heck wrote:

> Hi Peter,
>
> Thanks for the long reply! I have a few questions and a suggestion 
> (inline).
>
> Peter Reilly wrote:
>
>> Sorry Gus,
>> I meant to respond earlier but I am moving to a new computer
>> and some things got lost.
>>
>> The main reason the example is confusing is that macrodef, despite
>> it's name, does not do real textual  substition, it sees a Dom like tree
>> of elements (a tree of UnknownElements with paired RuntimeConfigurables)
>> and not the raw texual angley brackets.
>>
>> A simple example should make this clear. Assume first that namespaced
>> xml does not support prefix free elements and there is a class A
>>
>> class A {
>>   public void addFileSet(FileSet s) {}
>> }
>>
>> and class B:
>> class B {
>>   public void addFileSet(FileSet s) {}
>> }
>>
>> Now suppose that A is a type a in namespace antlib:net.a
>> and B is a type b in namespace antlib:net.b.
>> The following would be correct xml:
>> <ant:project xmlns:a="antlib:net.a" xmlns:b="antlib:net.b"
>>             xmnls:ant="antlib:org.apache.tools.ant">
>>  <a:a>
>>     <a:fileset dir="."/>
>>  </a:a>
>>
>>  <b:b>
>>     <b:fileset dir="."/>
>>   </b:b>
>>
>> </ant:project>
>
>
> This makes sense, and only requires that the program (ant) has either 
> separate fileset implementations ot associate with <a:fileset> and 
> <b:fileset> or has some way of knowing that both are equivalent to 
> <ant:fileset>. All the name spaces are specified explicitly and so 
> there is no confusion here.
>
>>
>> Now it is understandable that the following does not work:
>>
>>  <ant:macrodef name="c">
>>     <ant:element name="files">
>>     <ant:sequential>
>>         <a:a>
>>           <ant:files/>
>>          </a:a>
>>          <b:b>
>>            <ant:files/>
>>          </b:b>
>>     </ant:sequential>
>>  </ant:macrodef>
>>
>> as there is no set of xml dom elements that will be
>> acceptable to a:a and b:a.
>>
>> XmlNs does have the concept of a default ns prefix and a not 
>> specified ns prefix.
>>
>> First the default ns prefix.
>> This is what Dominique tried to use.
>>
>>  <ant:macrodef name="c">
>>     <ant:element name="files">
>>     <ant:sequential>
>>         <a xmlns="antlib:net.a">
>>           <ant:files/>
>>          </a>
>>          <b xmlns="antlib:net.b">
>>            <ant:files/>
>>          </b>
>>     </ant:sequential>
>>  </ant:macrodef>
>>
>>  <ant:c>
>>    <files xmlns="antlib:org.apache.tools.ant">
>>       <fileset dir="."/>
>>     </files>
>>   </ant:c>
>>
>> This will fail as the fileset element is in the 
>> "antlib:org.apache.tools.ant"
>> namespace and not in the "antlib:net.a" or "antlib:net.b" namespaces.
>>
>> The not-specified ns prefix (I think) is proceeded by whatever the 
>> xml processor
>> program likes. Ant treats this like a default xml namespace.
>> One solution to this (common) problem would be to allow the a's 
>> addFileset method
>> to be matched by both a's namespace fileset element and by ant's 
>> default namespace,
>> so the following would be correct xml:
>>
>> <a:a>
>>  <ant:fileset dir="."/>  <!-- matches A.addFileSet() method -->
>> </a:a>
>>
>> and
>> <a:a>
>>  <a:fileset dir="."/>  <!-- also matches A.addFileSet() method -->
>> </a:a>
>>
> I assume you are refering to A.addFileSet(o.a.t.a.types.FileSet fs) 
> above.
>
> One could conceive of A.addFileSet(com.astuff.FileSet fs) too for 
> custom fileset implementations. A might even have both methods. In 
> this case it seems ant should match different methods. It seems that 
> this should be theroetically possible but, the issue is what happens 
> when ant runs into
>
> <project xmlns:a="antlib:net.a">
> <a:a>
>  <fileset dir="."/>
> <a:a/>
> </project>
>
> I think this has to be equivalent to your first example above and
>
> <project>
> <a xmlns="antlib:net.a">
>  <fileset dir="."/>
> <a/>
> </project>
>
> must be equivalent to your second example right?
>
>>
>> On a side - issue, I think that this solution would help in the
>> uptake of antlibs.
>>
>> ForExample,
>> a a build file using ant-contrib with ant 1.5 would look like something
>> like this:
>>
>> <project default="compile">
>>  <taskdef resource="net/sf/antcontrib/antcontrib.properties"/>
>>    <switch value="${foo}">
>>    <case value="bar">
>>      <echo message="The value of property foo is bar" />
>>    </case>
>>    <case value="baz">
>>       <echo message="The value of property foo is baz" />
>>    </case>
>>    <default>
>>      <echo message="The value of property foo is not sensible" />
>>    </default>
>>  </switch>
>> </project>
>>
>> Using antlib:net.sf.antcontrib namespace in ant 1.6 results in 2/3 
>> lines being changed, and one feels like an xml ns lawyer  :-( Using 
>> the more relaxed rules only involves modifing a couple of lines
>> and looks (IMO) a lot nicer:
>>
>> <project default="compile" xmlns:ac="antlib:net.sf.antcontrib">
>>  <ac:switch value="${foo}">
>>    <case value="bar">
>>      <echo message="The value of property foo is bar" />
>>    </case>
>>    <case value="baz">
>>       <echo message="The value of property foo is baz" />
>>    </case>
>>    <default>
>>      <echo message="The value of property foo is not sensible" />
>>    </default>
>>  </ac:switch>
>> </project>
>>
> What this xml says to me (and I suspect says to the parser) is that 
> ac:switch is using ant:case elements. If the result is otherwise this 
> doesn't seem to follow the principal of least supprise. (imho)
>
> One could be explicit and only change 1/3 the lines by using:
>
> <project default="compile" xmlns:ant="antlib:org.apache.tools.ant">
> <switch value="${foo}" xmlns="antlib:net.sf.antcontrib">
>   <case value="bar">
>     <ant:echo message="The value of property foo is bar" />
>   </case>
>   <case value="baz">
>      <ant:echo message="The value of property foo is baz" />
>   </case>
>   <default>
>     <ant:echo message="The value of property foo is not sensible" />
>   </default>
> </ac:switch>
> </project>
>
> However, in real use switch probably does more than just echo on each 
> line and so this is of limited help.
>
> Unless I am confused, in your example ant would wind up looking for 
> ant:case, and when it didn't find it it would have to poll the 
> containing elements for their name spaces and search those name spaces 
> or just search all available name spaces for a matching element. (the 
> latter of which obviously runs into problems).
>
> If we do take a shortcut I think it would be better to do it like this:
>
> <project default="compile">
> <switch value="${foo}" xmlns="antlib:net.sf.antcontrib">
>   <case value="bar">
>     <echo message="The value of property foo is bar" />
>   </case>
>   <case value="baz">
>      <echo message="The value of property foo is baz" />
>   </case>
>   <default>
>     <echo message="The value of property foo is not sensible" />
>   </default>
> </ac:switch>
> </project>
>
> This version means that ant looks for an echo element in the 
> antcontrib name space. Upon not finding an ant contrib echo it only 
> has to look in ant core for an alternate echo element  and if it 
> doesn't have either an antcontrib echo or an ant echo, it should fail. 
> Basically what we have here is the condition that all antlibs inherit 
> (but may overide) ant core tasks.
>
> I like that one better, what do you think?
>
> Gus
>
>> Peter
>>
>> Gus Heck wrote:
>>
>>> Gus Heck wrote:
>>>
>>>>
>>>>> re given to the elements in the calling macro.
>>>>>
>>>>> In ant 1.6.0+, the namespace of elements discovered by reflection 
>>>>> take
>>>>> the namespace uri of type/task that contains the element (Note 
>>>>> this is the
>>>>> uri and not the localname).
>>>>>
>>>>> The enclosed patch will allow the namespace of nested elements to be
>>>>> either the ant uri or the namespace of the containing type/task.
>>>>>
>>>> I am quite possibly confused but I thought xml namespaces required 
>>>> that the namespace be
>>>>
>>>> 1. The namespace indicated by the specified prefix
>>>>
>>>> 2. If there is no prefix, then the current default namespace must 
>>>> be used. (defined on some containing element, be it the top element 
>>>> or some other containing element with an xmlns="some uri").
>>>>
>>>> If an unprefixed element is taking on a namespace not declared with 
>>>> xmlns="some uri" (which is different from xmlns:foo="some uri") 
>>>> aren't we blowing it in implementing namespaces correctly?
>>>>
>>>> -Gus
>>>>
>>> Was I wrong? Confused? or just ignored?
>>>
>>> -Gus
>>>
>>>>
>>>> ---------------------------------------------------------------------
>>>> To unsubscribe, e-mail: dev-unsubscribe@ant.apache.org
>>>> For additional commands, e-mail: dev-help@ant.apache.org
>>>>
>>>
>>>
>>>
>>> ---------------------------------------------------------------------
>>> To unsubscribe, e-mail: dev-unsubscribe@ant.apache.org
>>> For additional commands, e-mail: dev-help@ant.apache.org
>>>
>>>
>>>
>>
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: dev-unsubscribe@ant.apache.org
>> For additional commands, e-mail: dev-help@ant.apache.org
>>
>
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@ant.apache.org
> For additional commands, e-mail: dev-help@ant.apache.org
>
>
>


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@ant.apache.org
For additional commands, e-mail: dev-help@ant.apache.org


Mime
View raw message