001    /*
002     * Copyright 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.tools.info;
020    
021    import java.io.File;
022    import java.io.IOException;
023    import java.io.FileNotFoundException;
024    import java.io.FileInputStream;
025    import java.io.BufferedInputStream;
026    import java.net.URI;
027    import java.net.URL;
028    import java.util.Properties;
029    import java.util.Arrays;
030    
031    import net.dpml.transit.Artifact;
032    import net.dpml.transit.Transit;
033    
034    import net.dpml.util.ElementHelper;
035    
036    import org.w3c.dom.Element;
037    
038    /**
039     * Utility class used for construction of a module model from an XML source.
040     *
041     * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
042     * @version 1.1.3
043     */
044    public final class BuilderDirectiveHelper
045    {
046        private static final String BUILDER_ELEMENT_NAME = "builder";
047        private static final String LISTENERS_ELEMENT_NAME = "listeners";
048        private static final String LISTENER_ELEMENT_NAME = "listener";
049        private static final String PROPERTIES_ELEMENT_NAME = "properties";
050        private static final String PROPERTY_ELEMENT_NAME = "property";
051        
052        private BuilderDirectiveHelper()
053        {
054            // static utility class
055        }
056        
057       /**
058        * Construct a builder directive from XML source.
059        * @param source the XML source file
060        * @return the builder directive
061        * @exception IOException if an IO exception occurs
062        */
063        public static BuilderDirective build( File source ) throws IOException
064        {
065            if( null == source )
066            {
067                throw new NullPointerException( "source" );
068            }
069            if( !source.exists() )
070            {
071                throw new FileNotFoundException( source.toString() );
072            }
073            if( source.isDirectory() )
074            {
075                final String error = 
076                  "File ["
077                  + source 
078                  + "] references a directory.";
079                throw new IllegalArgumentException( error );
080            }
081            FileInputStream input = new FileInputStream( source );
082            BufferedInputStream buffer = new BufferedInputStream( input );
083            try
084            {
085                final Element root = ElementHelper.getRootElement( input );
086                File base = source.getParentFile();
087                return buildMainDirective( root );
088            }
089            catch( Throwable e )
090            {
091                final String error = 
092                  "An error occured while attempting to create the builder configuration from the source: "
093                  + source;
094                IOException ioe = new IOException( error );
095                ioe.initCause( e );
096                throw ioe;
097            }
098            finally
099            {
100                input.close();
101            }
102        }
103    
104       /**
105        * Creates a builder configuration using the default configuration.
106        * @return the builder directive
107        * @exception Exception if an error occurs
108        */
109        public static BuilderDirective build() throws Exception
110        {
111            File prefs = Transit.DPML_PREFS;
112            File config = new File( prefs, "dpml/tools/xmls/builder.xml" );
113            if( config.exists() )
114            {
115                return build( config );
116            }
117            else
118            {
119                final String error = 
120                  "Missing builder configuration: " 
121                  + config;
122                throw new FileNotFoundException( error );
123            }
124        }
125        
126        private static URL convertToURL( URI uri ) throws Exception
127        {
128            if( Artifact.isRecognized( uri ) )
129            {
130                Artifact artifact = Artifact.createArtifact( uri );
131                return artifact.toURL();
132            }
133            else
134            {
135                return uri.toURL();
136            }
137        }
138        
139       /**
140        * Build the configuration using an XML element.
141        * @param element the builder root element
142        * @return the builder directive
143        * @exception IOException if an I/O error occurs
144        */
145        private static BuilderDirective buildMainDirective( Element element ) throws Exception
146        {
147            final String elementName = element.getTagName();
148            if( !BUILDER_ELEMENT_NAME.equals( elementName ) )
149            {
150                final String error =
151                  "Element is not a builder configuration.";
152                throw new IllegalArgumentException( error );
153            }
154            
155            // get type descriptors, modules and properties
156            
157            String phase = ElementHelper.getAttribute( element, "default", "installation" );
158            
159            Properties properties = null;
160            ListenerDirective[] listeners = new ListenerDirective[0];
161            Element[] children = ElementHelper.getChildren( element );
162            for( int i=0; i<children.length; i++ )
163            {
164                Element child = children[i];
165                final String tag = child.getTagName();
166                if( PROPERTIES_ELEMENT_NAME.equals( tag ) )
167                {
168                    properties = buildProperties( child );
169                }
170                else if( LISTENERS_ELEMENT_NAME.equals( tag ) ) 
171                {
172                    listeners = buildListenerDirectives( child );
173                }
174                else
175                {
176                    final String error = 
177                      "Illegal element name [" + tag + "] within 'builder' element.";
178                    throw new IllegalArgumentException( error );
179                }
180            }
181            Arrays.sort( listeners );
182            return new BuilderDirective( listeners, phase, properties );
183        }
184        
185        private static ListenerDirective[] buildListenerDirectives( Element element ) throws Exception
186        {
187            Element[] children = ElementHelper.getChildren( element );
188            ListenerDirective[] types = new ListenerDirective[ children.length ];
189            for( int i=0; i<children.length; i++ )
190            {
191                Element child = children[i];
192                types[i] = buildListenerDirective( child );
193            }
194            return types;
195        }
196        
197        private static ListenerDirective buildListenerDirective( Element element ) throws Exception
198        {
199            final String tag = element.getTagName();
200            if( LISTENER_ELEMENT_NAME.equals( tag ) )
201            {
202                final String name = ElementHelper.getAttribute( element, "name", null );
203                final String spec = ElementHelper.getAttribute( element, "uri", null );
204                final URI uri = createURI( spec );
205                final String classname = ElementHelper.getAttribute( element, "class", null );
206                final String value = ElementHelper.getAttribute( element, "priority", null );
207                int priority = Integer.parseInt( value );
208                final String deps = ElementHelper.getAttribute( element, "depends", null );
209                final Properties properties = buildProperties( element );
210                return new ListenerDirective( name, priority, uri, classname, properties );
211            }
212            else
213            {
214                final String error = 
215                  "Invalid resource element name [" 
216                  + tag
217                  + "].";
218                throw new IllegalArgumentException( error );
219            }
220        }
221        
222        private static Properties buildProperties( Element element )
223        {
224            Properties properties = new Properties();
225            Element[] children = ElementHelper.getChildren( element );
226            for( int i=0; i<children.length; i++ )
227            {
228                Element child = children[i];
229                String tag = child.getTagName();
230                if( PROPERTY_ELEMENT_NAME.equals( tag ) )
231                {
232                    String key = ElementHelper.getAttribute( child, "name", null );
233                    if( null == key )
234                    {
235                        final String error =
236                          "Property declaration does not contain a 'name' attribute.";
237                        throw new IllegalArgumentException( error );
238                    }
239                    else
240                    {
241                        String value = ElementHelper.getAttribute( child, "value", null );
242                        properties.setProperty( key, value );
243                    }
244                }
245            }
246            return properties;
247        }
248        
249        private static URI createURI( String spec ) throws Exception
250        {
251            if( null == spec )
252            {
253                return null;
254            }
255            else
256            {
257                return new URI( spec );
258            }
259        }
260        
261    }