001    /*
002     * Copyright 2006 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.util;
020    
021    import java.io.IOException;
022    import java.net.URI;
023    import java.util.Map;
024    import java.util.Hashtable;
025    
026    import net.dpml.lang.Part;
027    import net.dpml.lang.PartDecoder;
028    
029    import net.dpml.transit.Artifact;
030    
031    import org.w3c.dom.Element;
032    import org.w3c.dom.TypeInfo;
033    
034    /**
035     * Utility class supporting resolution of element decoders.
036     */
037    public final class DecoderFactory
038    {
039        private static final String PART_XSD_URI = "@PART-XSD-URI@";
040    
041        private final Map m_map; // maps namespace uris to element handlers
042        
043       /**
044        * Creation of a new helper factory using default mappings.
045        */
046        public DecoderFactory()
047        {
048            this( null );
049        }
050        
051       /**
052        * Creation of a new decoder factory. The supplied map contains the mapping
053        * of namespace urn to decoder plugin uris.  If the "dpml/lang/dpml-part" namespace
054        * is not included in the map a special uri will be assigned associating the namespace
055        * with this package implementation.
056        *
057        * @param map the namespace to helper uri map
058        */
059        public DecoderFactory( Map map )
060        {
061            if( null == map )
062            {
063                m_map = new Hashtable();
064            }
065            else
066            {
067                m_map = map;
068            }
069        }
070        
071       /**
072        * Get an element helper based on the namespace declared by the supplied element. If 
073        * the element namespace is the dpml/part namespace then a local uri is returned, 
074        * otherwise evaluation is based on namespace to hanlder mappings supplied to 
075        * the factory constructor.  If a map entry is resolved, a delegating builder is 
076        * established with the resolved helper uri, otherwise a helper uri is resolved 
077        * by substituting the namespace uri artifact type for "part" on the assumption that 
078        * a part implemenation will be available.
079        *
080        * @param element the DOM element
081        * @return the decoder
082        * @exception Exception if an eror occurs
083        */
084        public Decoder loadDecoder( Element element ) throws Exception
085        {
086            TypeInfo info = element.getSchemaTypeInfo();
087            String namespace = info.getTypeNamespace();
088            if( PART_XSD_URI.equals( namespace ) )
089            {
090                return PartDecoder.getInstance();
091            }
092            else
093            {
094                URI uri = getDecoderURI( element );
095                return new DelegatingDecoder( this, uri );
096            }
097        }
098        
099       /**
100        * Resolve the decoder uri from a supplied element.
101        *
102        * @param element the DOM element
103        * @return the decoder uri
104        * @exception Exception if an error occurs
105        */
106        public URI getDecoderURI( Element element ) throws Exception
107        {
108            String uri = ElementHelper.getAttribute( element, "handler" );
109            if( null != uri )
110            {
111                try
112                {
113                    return new URI( uri );
114                }
115                catch( Exception e )
116                {
117                    final String error = 
118                      "Internal error while resolving handler attribute (expecting uri value)";
119                    throw new DecodingException( element, error, e );
120                }
121            }
122            TypeInfo info = element.getSchemaTypeInfo();
123            String namespace = info.getTypeNamespace();
124            if( m_map.containsKey( namespace ) )
125            {
126                return (URI) m_map.get( namespace );
127            }
128            else
129            {
130                return getDecoderURIFromNamespaceURI( namespace );
131            }
132        }
133        
134       /**
135        * Resolve the part handler given an element namespace.
136        * @param urn the namespace value
137        * @return the decoder uri
138        * @exception Exception if an error occurs
139        */
140        public static URI getDecoderURIFromNamespaceURI( String urn ) throws Exception
141        {
142            URI raw = new URI( urn );
143            Artifact artifact = Artifact.createArtifact( raw );
144            String scheme = artifact.getScheme();
145            String group = artifact.getGroup();
146            String name = artifact.getName();
147            String type = artifact.getType();
148            String version = artifact.getVersion();
149            String path = "link:part:" + group + "/" + name;
150            Artifact link = Artifact.createArtifact( path );
151            return link.toURI();
152        }
153        
154       
155       /**
156        * Delegating builder that defers resolution until required.
157        */
158        private class DelegatingDecoder implements Decoder
159        {
160            private final DecoderFactory m_factory;
161            private final URI m_uri;
162            private Decoder m_delegate = null;
163            
164           /**
165            * Creation of a new delegating builder instance.
166            * @param uri the uri of the builder that operations will be delegated to
167            */
168            DelegatingDecoder( DecoderFactory factory, URI uri )
169            {
170                m_uri = uri;
171                m_factory = factory;
172            }
173            
174           /**
175            * Delegating implementation of the decode operation.
176            * @param element the subject element
177            * @return the resulting object
178            * @exception IOException if an IO error occurs
179            */
180            public Object decode( Element element, Resolver resolver ) throws IOException
181            {
182                Decoder decoder = getDelegateDecoder();
183                return decoder.decode( element, resolver );
184            }
185            
186            private Decoder getDelegateDecoder()
187            {
188                if( null != m_delegate )
189                {
190                    return m_delegate;
191                }
192                else
193                {
194                    Object instance = getInstance();
195                    if( instance instanceof Decoder )
196                    {
197                        m_delegate = (Decoder) instance;
198                        return m_delegate;
199                    }
200                    else
201                    {
202                        final String error = 
203                          "Object resolved from the uri does not provide decoding services."
204                          + "\nDelegate URI: " + m_uri
205                          + "\nDelegate Class: " + instance.getClass().getName();
206                        throw new IllegalStateException( error );
207                    }
208                }
209            }
210            
211            private Object getInstance()
212            {
213                try
214                {
215                    Object[] args = new Object[]{m_factory};
216                    Part part = Part.load( m_uri );
217                    return part.instantiate( args );
218                }
219                catch( Throwable e )
220                {
221                    final String error = 
222                      "Unexpected error occured while attempting to establish the delegate decoder."
223                      + "\nDelegate URI: " + m_uri;
224                    throw new RuntimeException( error, e ); // change to a factory exception
225                }
226            }
227        }
228        
229        private static URI createURI( String spec )
230        {
231            try
232            {
233                return new URI( spec );
234            }
235            catch( Throwable e )
236            {
237                e.printStackTrace();
238                return null;
239            }
240        }
241    }