001    /*
002     * Copyright (c) 2005 Stephen J. McConnell
003     *
004     * Licensed  under the  Apache License,  Version 2.0  (the "License");
005     * you may not use  this file  except in  compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *   http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed  under the  License is distributed on an "AS IS" BASIS,
012     * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
013     * implied.
014     *
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    
019    package net.dpml.metro.tools;
020    
021    import java.io.IOException;
022    import java.util.LinkedList;
023    import java.util.List;
024    import java.beans.IntrospectionException;
025    
026    import net.dpml.metro.info.Type;
027    import net.dpml.metro.info.PartReference;
028    
029    import net.dpml.tools.tasks.GenericTask;
030    
031    import org.apache.tools.ant.BuildException;
032    import org.apache.tools.ant.DynamicElementNS;
033    import org.apache.tools.ant.Location;
034    import org.apache.tools.ant.Project;
035    import org.apache.tools.ant.Task;
036    
037    /**
038     * A datatype that enables custom part builders.
039     *
040     * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
041     * @version 1.1.3
042     */
043    public class PartsDataType extends GenericTask implements DynamicElementNS
044    {
045        private List m_builders = new LinkedList();
046        private Task m_owner;
047    
048       /**
049        * Creation of a new parts data type.
050        * @param owner the owning task
051        */
052        public PartsDataType( Task owner )
053        {
054            Project proj = owner.getProject();
055            setProject( proj );
056            m_owner = owner;
057        }
058    
059       /**
060        * Create a new component builder task.
061        * @return a new component builder task
062        */
063        public ComponentBuilderTask createComponent()
064        {
065            ComponentBuilderTask builder = new ComponentBuilderTask();
066            m_builders.add( builder );
067            return builder;
068        }
069    
070       /**
071        * Create a new part datatype.
072        * @return the nwew part datatype
073        */
074        public PartDataType createPart()
075        {
076            PartDataType builder = new PartDataType();
077            m_builders.add( builder );
078            return builder;
079        }
080    
081       /**
082        * Create a new constructed value builder.
083        * @return a part builder
084        */
085        public EntryDataType createValue()
086        {
087            final EntryDataType builder = new EntryDataType();
088            m_builders.add( builder );
089            return builder;
090        }
091    
092       /**
093        * Operation used to construct a custom part type directive.
094        * @param uri the part handler uri
095        * @param name the element name
096        * @param qualified the qualified name
097        * @return a dynamic part builder
098        */
099        public Object createDynamicElement( String uri, String name, String qualified )
100        {
101            String path = getProject().replaceProperties( uri );
102            PartReferenceBuilder builder = loadPartHandler( path, name );
103            if( null != builder )
104            {
105                m_builders.add( builder );
106            }
107            return builder;
108        }
109    
110        private PartReferenceBuilder loadPartHandler( String uri, String name ) throws BuildException
111        {
112            String urn = uri + ":" + name;
113            Object builder = null;
114            PartReferenceBuilder partBuilder = null;
115            String target = m_owner.getOwningTarget().getName();
116            Location location = m_owner.getLocation();
117            ClassLoader context = Thread.currentThread().getContextClassLoader();
118            try
119            {
120                Thread.currentThread().setContextClassLoader( getClass().getClassLoader() );
121                Project proj = getProject();
122                builder = proj.createDataType( urn );
123                partBuilder  = (PartReferenceBuilder) builder;
124                return partBuilder;
125            }
126            catch( ClassCastException e )
127            {
128                final String error = 
129                  "The custom part builder ["
130                  + builder.getClass().getName()
131                  + "] established by the uri [" 
132                  + urn
133                  + "] declared by the element <"
134                  + name
135                  + "> under the task <"
136                  + m_owner.getTaskName() 
137                  + "><parts> within the target <"
138                  + target  
139                  + "> does not implement the net.dpml.metro.tools.PartReferenceBuilder interface.";
140                throw new BuildException( error, e );
141            }
142            catch( BuildException e )
143            {
144                final String error = 
145                  "Unable to load the plugin from the uri [" 
146                  + urn 
147                  + "] to handle the custom part type declared by the element <"
148                  + name
149                  + "> within <"
150                  + m_owner.getTaskName() 
151                  + "><parts> under the target <"
152                  + target  
153                  + ">.";
154                throw new BuildException( error, e );
155            }
156            catch( Throwable e )
157            {
158                final String error = 
159                  "Unexpected exception while attempting to load the custom part handler with the uri [" 
160                  + urn 
161                  + "] declared by the element <"
162                  + name
163                  + "> within <"
164                  + m_owner.getTaskName() 
165                  + "><parts> under the target <"
166                  + target  
167                  + ">.";
168                throw new BuildException( error, e );
169            }
170            finally
171            {
172                Thread.currentThread().setContextClassLoader( context );
173            }
174        }
175    
176       /**
177        * Return the set of parts contained within this container.
178        * @return the contained parts
179        */
180        public PartReferenceBuilder[] getPartBuilders()
181        {
182            return (PartReferenceBuilder[]) m_builders.toArray( new PartReferenceBuilder[0] );
183        }
184    
185       /**
186        * Return the set of parts contained within this parts collection.
187        * @param classloader the runtime classloader
188        * @param type the component type that references are relative to
189        * @return the contained parts
190        * @exception IntrospectionException if a class introspection error occurs
191        * @exception IOException if an I/O error occurs
192        * @exception ClassNotFoundException if a part class cannot be found
193        */
194        public PartReference[] getParts( ClassLoader classloader, Type type ) 
195          throws IntrospectionException, IOException, ClassNotFoundException
196        {
197            PartReferenceBuilder[] builders = 
198              (PartReferenceBuilder[]) m_builders.toArray( new PartReferenceBuilder[0] );
199            PartReference[] parts = new PartReference[ builders.length ];
200            for( int i=0; i<builders.length; i++ )
201            {
202                PartReferenceBuilder builder = builders[i];
203                parts[i] = builder.buildPartReference( classloader, type );
204            }
205            return parts;
206        }
207    }