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.File;
022    import java.io.FileOutputStream;
023    import java.io.OutputStream;
024    import java.io.IOException;
025    import java.util.LinkedList;
026    import java.util.List;
027    import java.beans.IntrospectionException;
028    
029    import net.dpml.library.info.Scope;
030    
031    import net.dpml.metro.info.Type;
032    import net.dpml.metro.builder.ComponentTypeEncoder;
033    
034    import net.dpml.tools.tasks.GenericTask;
035    
036    import org.apache.tools.ant.AntClassLoader;
037    import org.apache.tools.ant.BuildException;
038    import org.apache.tools.ant.DynamicElementNS;
039    import org.apache.tools.ant.Project;
040    import org.apache.tools.ant.types.Path;
041    
042    /**
043     * Task that handles the construction of catalog of type entries.
044     *
045     * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
046     * @version 1.1.3
047     */
048    public class TypesTask extends GenericTask implements DynamicElementNS
049    {
050        private static final ComponentTypeEncoder COMPONENT_TYPE_ENCODER = 
051          new ComponentTypeEncoder();
052    
053    
054        private List m_builders = new LinkedList();
055    
056       /**
057        * Create and return a new type builder.
058        * @return the type builder
059        */
060        public TypeBuilder createType()
061        {
062            Project proj = getProject();
063            TypeBuilderTask builder = new TypeBuilderTask();
064            builder.setProject( proj );
065            m_builders.add( builder );
066            return builder;
067        }
068    
069       /**
070        * Operation used to construct a custom part type directive.
071        * @param uri the part handler uri
072        * @param name the element name
073        * @param qualified the qualified name
074        * @return a dynamic type builder
075        */
076        public Object createDynamicElement( String uri, String name, String qualified )
077        {
078            String path = getProject().replaceProperties( uri );
079            TypeBuilder builder = loadTypeBuilder( path, name );
080            if( null != builder )
081            {
082                m_builders.add( builder );
083            }
084            return builder;
085        }
086    
087       /**
088        * Task executaion.
089        */
090        public void execute()
091        {
092            Project proj = getProject();
093            Path path = getContext().getPath( Scope.RUNTIME );
094            File classes = getContext().getTargetClassesMainDirectory();
095            path.createPathElement().setLocation( classes );
096            ClassLoader classloader = new AntClassLoader( proj, path );
097            buildTypes( classloader );
098        }
099    
100        private void buildTypes( ClassLoader classloader )
101        {
102            final TypeBuilder[] builders = (TypeBuilder[]) m_builders.toArray( new TypeBuilder[0] );
103            ClassLoader current = Thread.currentThread().getContextClassLoader();
104            for( int i=0; i<builders.length; i++ )
105            {
106                final TypeBuilder builder = builders[i];
107                try
108                {
109                    final Type type = builder.buildType( classloader );
110                    OutputStream output = getOutputStream( type );
111                    try
112                    {
113                        COMPONENT_TYPE_ENCODER.export( type, output );
114                    }
115                    finally
116                    {
117                        try
118                        {
119                            output.close();
120                        }
121                        catch( IOException ioe )
122                        {
123                            ioe.printStackTrace();
124                        }
125                    }
126                }
127                catch( IntrospectionException e )
128                {
129                    final String error = e.getMessage();
130                    throw new BuildException( error, e, getLocation() );
131                }
132                catch( BuildException e )
133                {
134                    throw e;
135                }
136                catch( Throwable e )
137                {
138                    final String error = 
139                      "Internal error while attempting to build types."
140                      + "\nCause: " + e.getClass().getName()
141                      + "\nMessage: " + e.getMessage();
142                    throw new BuildException( error, e, getLocation() );
143                }
144            }
145        }
146        
147        private OutputStream getOutputStream( Type type ) throws IOException
148        {
149            final String classname = type.getInfo().getClassname();
150            final String resource = getEmbeddedResourcePath( classname );
151            final File file = getEmbeddedOutputFile( resource );
152            file.getParentFile().mkdirs();
153            return new FileOutputStream( file );
154        }
155    
156        private String getEmbeddedResourcePath( String classname )
157        {
158            String path = classname.replace( '.', '/' );
159            String filename = path + ".type";
160            return filename;
161        }
162    
163        private File getEmbeddedOutputFile( String filename )
164        {
165            File classes = getContext().getTargetClassesMainDirectory();
166            File destination = new File( classes, filename );
167            return destination;
168        }
169    
170        private TypeBuilder loadTypeBuilder( String uri, String name ) throws BuildException
171        {
172            String urn = uri + ":" + name;
173            Object builder = null;
174            TypeBuilder typeBuilder = null;
175            ClassLoader context = Thread.currentThread().getContextClassLoader();
176            try
177            {
178                Thread.currentThread().setContextClassLoader( getClass().getClassLoader() );
179                Project proj = getProject();
180                builder = proj.createDataType( urn );
181                typeBuilder  = (TypeBuilder) builder;
182                return typeBuilder;
183            }
184            catch( ClassCastException e )
185            {
186                final String error = 
187                  "The custom type builder ["
188                  + builder.getClass().getName()
189                  + "] established by the uri [" 
190                  + urn
191                  + "] declared by the element <"
192                  + name
193                  + "' does not implement the net.dpml.metro.builder.TypeBuilder interface.";
194                throw new BuildException( error, e, getLocation() );
195            }
196            catch( BuildException e )
197            {
198                final String error = 
199                  "Unable to load the plugin from the uri [" 
200                  + urn 
201                  + "] to handle the custom type declared by the element <"
202                  + name
203                  + ">.";
204                throw new BuildException( error, e, getLocation() );
205            }
206            catch( Throwable e )
207            {
208                final String error = 
209                  "Unexpected exception while attempting to load the custom type handler with the uri [" 
210                  + urn 
211                  + "] declared by the element <"
212                  + name
213                  + ">.";
214                throw new BuildException( error, e, getLocation() );
215            }
216            finally
217            {
218                Thread.currentThread().setContextClassLoader( context );
219            }
220        }
221    }