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 }