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 }