Source code: javax/ide/extension/spi/ExtensionVisitor.java
1 package javax.ide.extension.spi;
2
3 import java.net.URI;
4
5 import java.util.ArrayList;
6 import java.util.Collection;
7 import java.util.StringTokenizer;
8 import java.util.logging.Level;
9
10 import javax.ide.extension.ElementContext;
11 import javax.ide.extension.ElementEndContext;
12 import javax.ide.extension.ElementName;
13 import javax.ide.extension.ElementStartContext;
14 import javax.ide.extension.ElementVisitor;
15 import javax.ide.extension.ElementVisitorFactory;
16 import javax.ide.extension.Extension;
17 import javax.ide.extension.ExtensionHook;
18 import javax.ide.extension.I18NStringVisitor;
19
20 /**
21 * Visitor for the root JSR-198 extension element. This is the "entry point"
22 * for all manifest processing.
23 */
24 public abstract class ExtensionVisitor extends BaseExtensionVisitor
25 {
26
27 private static final ElementName NAME =
28 new ElementName( ExtensionHook.MANIFEST_XMLNS, "name" );
29 private static final ElementName OWNER =
30 new ElementName( ExtensionHook.MANIFEST_XMLNS, "owner" );
31 private static final ElementName HOOKS =
32 new ElementName( ExtensionHook.MANIFEST_XMLNS, "hooks" );
33 private static final ElementName CLASSPATHS =
34 new ElementName( ExtensionHook.MANIFEST_XMLNS, "classpaths" );
35 private static final ElementName CLASSPATH =
36 new ElementName( ExtensionHook.MANIFEST_XMLNS, "classpath" );
37
38 private ElementVisitor _nameVisitor = createNameVisitor();
39 private ElementVisitor _ownerVisitor = createOwnerVisitor();
40 private ElementVisitor _hooksVisitor = createHooksVisitor();
41 private ElementVisitor _classpathsVisitor = createClasspathsVisitor();
42 private ElementVisitor _classpathVisitor = createClasspathVisitor();
43 private ElementVisitor _dependenciesVisitor = createDependenciesVisitor();
44
45 /**
46 * The key for the current <tt>ExtensionSource</tt> instance in the scope
47 * data map.
48 */
49 public static final String KEY_EXTENSION_SOURCE = "extSource";
50
51 /**
52 * The key for the <tt>ClassLoader</tt> to be used to when looking up classes
53 * for the current extension. This is used by I18NStringVisitor /
54 * I18NCharVisitor. If no classloader is in the scope map, the context
55 * classloader of the current thread is used.
56 */
57 public static final String KEY_CLASSLOADER = "classLoader";
58
59 private Collection _extensions = new ArrayList();
60
61 private final ElementVisitorFactory _hookVisitorFactory;
62
63 protected ExtensionVisitor( ElementVisitorFactory hookFactory )
64 {
65 _hookVisitorFactory = hookFactory;
66 }
67
68 public final Collection getExtensions()
69 {
70 return _extensions;
71 }
72
73 public final void start( ElementStartContext context )
74 {
75 Extension ext = processExtension( context );
76 if ( ext == null )
77 {
78 return;
79 }
80
81 context.getScopeData().put( ExtensionVisitor.KEY_CLASSLOADER,
82 getClassLoader( ext ) );
83
84 String rsbundleClass = context.getAttributeValue( "rsbundle-class" );
85 if ( rsbundleClass != null &&
86 ( rsbundleClass = rsbundleClass.trim()) != "" )
87 {
88 context.getScopeData().put( ExtensionHook.KEY_RSBUNDLE_CLASS,
89 rsbundleClass );
90 }
91
92 context.registerChildVisitor( NAME, _nameVisitor );
93 context.registerChildVisitor( OWNER, _ownerVisitor );
94 context.registerChildVisitor( HOOKS, _hooksVisitor );
95 context.registerChildVisitor( CLASSPATHS, _classpathsVisitor );
96 context.registerChildVisitor( DependenciesVisitor.ELEMENT,
97 _dependenciesVisitor );
98 }
99
100 public final void end( ElementEndContext end )
101 {
102 _extensions.add( (Extension) end.getScopeData().get( ExtensionHook.KEY_EXTENSION ));
103 }
104
105
106 protected final DefaultExtension getExtension( ElementContext context )
107 {
108 return (DefaultExtension) context.getScopeData().get(
109 ExtensionHook.KEY_EXTENSION );
110 }
111
112 protected final ExtensionSource getSource( ElementContext context )
113 {
114 return (ExtensionSource) context.getScopeData().get(
115 KEY_EXTENSION_SOURCE );
116 }
117
118 protected ElementVisitor createNameVisitor()
119 {
120 return new I18NStringVisitor()
121 {
122 public void string( ElementContext context, String value )
123 {
124 getExtension( context ).setName( value );
125 }
126 };
127 }
128
129 protected ElementVisitor createOwnerVisitor()
130 {
131 return new I18NStringVisitor()
132 {
133 public void string( ElementContext context, String value )
134 {
135 getExtension( context ).setOwner( value );
136 }
137 };
138 }
139
140 protected ElementVisitor createClasspathsVisitor()
141 {
142 return new ClasspathsVisitor();
143 }
144
145 protected ElementVisitor createClasspathVisitor()
146 {
147 return new ClasspathVisitor();
148 }
149
150 protected ElementVisitor createHooksVisitor()
151 {
152 return new HooksVisitor();
153 }
154
155 protected ElementVisitor createDependenciesVisitor()
156 {
157 return new DependenciesVisitor();
158 }
159
160 private final class ClasspathsVisitor extends ElementVisitor
161 {
162 public void start( ElementStartContext context )
163 {
164 context.registerChildVisitor( CLASSPATH, _classpathVisitor );
165 }
166 }
167
168 private final class ClasspathVisitor extends ElementVisitor
169 {
170 public void end( ElementEndContext context )
171 {
172 String text = context.getText().trim();
173 if ( text.length() == 0 )
174 {
175 log( context, Level.WARNING,
176 "Empty classpath definition"
177 );
178 }
179
180 Extension ext = getExtension( context );
181
182 // Check for path separators ( ';', ':', or the system path separator
183 // in case it is neither : nor ; )
184 char[] seps = new char[] { ':', ';',
185 System.getProperty( "path.separator").charAt( 0 ) };
186 for ( int i=0; i < seps.length; i++ )
187 {
188 char sep = seps[ i ];
189 int sepIdx = text.indexOf( sep );
190 if ( sepIdx > 0 )
191 {
192 StringTokenizer tok = new StringTokenizer( text,
193 String.valueOf( sep ) );
194 while ( tok.hasMoreTokens() )
195 {
196 String token = tok.nextToken();
197 URI abs = getSource( context ).resolvePath( ext, token );
198 addToClasspath( ext, abs );
199 }
200 // If we found a path separator, we're done.
201 return;
202 }
203 }
204
205 // OK, this seems to be a single entry.
206 addToClasspath( ext,
207 getSource( context ).resolvePath( ext, text ) );
208 }
209 }
210
211 private final class HooksVisitor extends ElementVisitor
212 {
213 public void start( ElementStartContext context )
214 {
215 context.registerVisitorFactory( _hookVisitorFactory );
216 }
217 }
218
219 /**
220 * Get the class loader that should be used by default to load an
221 * extension.<p>
222 *
223 * This implementation returns Thread.currentThread().getContextClassLoader().
224 *
225 * @param extension the extension being processed.
226 * @return the classloader to use to load resources for the specified
227 * extension.
228 */
229 protected ClassLoader getClassLoader( Extension extension )
230 {
231 return Thread.currentThread().getContextClassLoader();
232 }
233
234 /**
235 * Add the specified entry to the classpath of the class loader which is
236 * loading this extension and its dependent classes.
237 *
238 * @param ext the extension being processed. This may be in a partially
239 * initialized state.
240 * @param entry a classpath entry used by the current extension.
241 */
242 protected abstract void addToClasspath( Extension ext, URI entry );
243 }