Mercurial Hosting > nabble
comparison src/nabble/modules/ModuleManager.java @ 0:7ecd1a4ef557
add content
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Thu, 21 Mar 2019 19:15:52 -0600 |
parents | |
children | e0c501fb5229 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:7ecd1a4ef557 |
---|---|
1 package nabble.modules; | |
2 | |
3 import fschmidt.util.java.CollectionUtils; | |
4 import fschmidt.util.java.IoUtils; | |
5 import nabble.model.Site; | |
6 import nabble.modules.hacks.HacksModule; | |
7 import nabble.modules.poll.PollModule; | |
8 import nabble.modules.workgroup.WorkgroupModule; | |
9 import nabble.naml.compiler.CompileException; | |
10 import nabble.naml.compiler.Macro; | |
11 import nabble.naml.compiler.Module; | |
12 import nabble.naml.compiler.Program; | |
13 import nabble.naml.compiler.Source; | |
14 import nabble.naml.compiler.StackTrace; | |
15 import nabble.naml.compiler.StackTraceElement; | |
16 import nabble.naml.compiler.Template; | |
17 import nabble.naml.compiler.TemplatePrintWriter; | |
18 import nabble.naml.compiler.TemplateRuntimeException; | |
19 import nabble.naml.dom.Attribute; | |
20 import nabble.naml.dom.Element; | |
21 import nabble.naml.dom.ElementName; | |
22 import org.slf4j.Logger; | |
23 import org.slf4j.LoggerFactory; | |
24 | |
25 import javax.servlet.ServletException; | |
26 import java.io.IOException; | |
27 import java.io.Writer; | |
28 import java.net.URL; | |
29 import java.util.ArrayList; | |
30 import java.util.Arrays; | |
31 import java.util.Collection; | |
32 import java.util.Collections; | |
33 import java.util.LinkedHashMap; | |
34 import java.util.HashSet; | |
35 import java.util.Iterator; | |
36 import java.util.List; | |
37 import java.util.Map; | |
38 import java.util.Set; | |
39 | |
40 | |
41 public final class ModuleManager { | |
42 private static final Logger logger = LoggerFactory.getLogger(ModuleManager.class); | |
43 | |
44 private static final Map<String,ModuleInfo> MODULES = new LinkedHashMap<String,ModuleInfo>(); | |
45 | |
46 private static class ModuleInfo { | |
47 final Module module; | |
48 final boolean isEnabledByDefault; | |
49 | |
50 ModuleInfo(Module module,boolean isEnabledByDefault) { | |
51 this.module = module; | |
52 this.isEnabledByDefault = isEnabledByDefault; | |
53 MODULES.put( module.getName(), this ); | |
54 } | |
55 } | |
56 | |
57 static { | |
58 new ModuleInfo(WorkgroupModule.INSTANCE,false); | |
59 new ModuleInfo(PollModule.INSTANCE,true); | |
60 new ModuleInfo(HacksModule.INSTANCE,false); | |
61 } | |
62 | |
63 private static final String CUSTOM_TWEAK_PREFIX = "custom_tweak:"; | |
64 public static final String CONFIGURATION_TWEAK = "configuration"; | |
65 | |
66 private static List<Module> getBaseModules() { | |
67 List<Module> modules = new ArrayList<Module>(); | |
68 modules.add(SOURCE_MODULE); | |
69 return modules; | |
70 } | |
71 | |
72 public static List<Module> getGenericModules() { | |
73 List<Module> modules = getBaseModules(); | |
74 for( ModuleInfo mi : MODULES.values() ) { | |
75 if( mi.isEnabledByDefault ) | |
76 modules.add( mi.module ); | |
77 } | |
78 modules = sort(modules); | |
79 return modules; | |
80 } | |
81 | |
82 public static List<Module> getModules(Site site) { | |
83 List<Module> modules = getBaseModules(); | |
84 for( ModuleInfo mi : MODULES.values() ) { | |
85 if( site.isModuleEnabled(mi.module.getName()) ) { | |
86 modules.add( mi.module ); | |
87 } | |
88 } | |
89 modules = sort(modules); | |
90 if( site.getTweakException() == null ) { | |
91 String config = site.getConfigurationTweak(); | |
92 if( config.length() > 0 ) { | |
93 Source source = Source.getInstance(CONFIGURATION_TWEAK,config); | |
94 Module module = new NamlModule( "config", Collections.singleton(source), Collections.<String>emptySet() ); | |
95 modules.add(module); | |
96 } | |
97 Map<String,String> tweaks = site.getCustomTweaks(); | |
98 if( !tweaks.isEmpty() ) { | |
99 List<Source> sources = new ArrayList<Source>(); | |
100 for( Map.Entry<String,String> entry : tweaks.entrySet() ) { | |
101 String name = entry.getKey(); | |
102 String content = entry.getValue(); | |
103 Source source = Source.getInstance(CUSTOM_TWEAK_PREFIX+name,content); | |
104 sources.add(source); | |
105 } | |
106 Module module = new NamlModule( "tweak", sources, Collections.<String>emptySet() ); | |
107 modules.add(module); | |
108 } | |
109 } | |
110 return modules; | |
111 } | |
112 | |
113 private static List<Module> sort(List<Module> modules) { | |
114 List<Module> rtn = new ArrayList<Module>(); | |
115 Set<String> names = new HashSet<String>(); | |
116 while( !modules.isEmpty() ) { | |
117 boolean changed = false; | |
118 for( Iterator<Module> iter = modules.iterator(); iter.hasNext(); ) { | |
119 Module m = iter.next(); | |
120 if( names.containsAll(m.getDependencies()) ) { | |
121 rtn.add(m); | |
122 names.add(m.getName()); | |
123 iter.remove(); | |
124 changed = true; | |
125 } | |
126 } | |
127 if( !changed ) | |
128 throw new RuntimeException("circular dependencies: "+modules); | |
129 } | |
130 return rtn; | |
131 } | |
132 | |
133 public static Collection<Source> loadSource(Module module) { | |
134 String moduleName = module.getName(); | |
135 try { | |
136 return Collections.singleton( | |
137 Source.getInstance( | |
138 moduleName + ":" + moduleName + ".naml", | |
139 IoUtils.read( ClassLoader.getSystemResource( | |
140 module.getClass().getPackage().getName().replace('.','/') + '/' + moduleName+".naml" | |
141 ) ) | |
142 ) | |
143 ); | |
144 } catch(IOException e) { | |
145 throw new RuntimeException(e); | |
146 } | |
147 } | |
148 | |
149 | |
150 private static List<URL> listNamlFiles(String path) { | |
151 List<URL> list = new ArrayList<URL>(); | |
152 for( URL url : IoUtils.getResources(ModuleManager.class) ) { | |
153 String s = url.toString(); | |
154 if( s.endsWith(".naml") && s.indexOf(path) != -1 ) | |
155 list.add(url); | |
156 } | |
157 return list; | |
158 } | |
159 | |
160 private static final ElementName DEPENDENCY = new ElementName("dependency"); | |
161 private static final Set<ElementName> IGNORE_TAGS = Collections.singleton(DEPENDENCY); | |
162 private static final Set<String> DEFAULT_ON = new HashSet<String>(); | |
163 static { | |
164 DEFAULT_ON.add("responsive"); | |
165 DEFAULT_ON.add("ads"); | |
166 } | |
167 | |
168 static { | |
169 try { | |
170 for( URL url : listNamlFiles("/nabble/modules/naml/") ) { | |
171 String s = url.toString(); | |
172 String name = s.substring(s.lastIndexOf('/')+1); | |
173 name = name.substring(0,name.length()-5); | |
174 Source source = Source.getInstance( name + ":" + name + ".naml", IoUtils.read(url), IGNORE_TAGS ); | |
175 Set<String> dependencies = new HashSet<String>(); | |
176 StackTrace stackTrace = new StackTrace(); | |
177 for( Object obj : source.parse() ) { | |
178 if( obj instanceof Element ) { | |
179 Element element = (Element)obj; | |
180 if( element.name().equals(DEPENDENCY) ) { | |
181 stackTrace.push( new StackTraceElement(source,element) ); | |
182 try { | |
183 Attribute attr = element.getAttribute("module"); | |
184 if( attr == null ) | |
185 throw new CompileException(stackTrace,"module attribute required"); | |
186 String dependency = attr.value().toString(); | |
187 dependencies.add(dependency); | |
188 } finally { | |
189 stackTrace.pop(); | |
190 } | |
191 } | |
192 } | |
193 } | |
194 Module module = new NamlModule( name, Collections.singleton(source), CollectionUtils.optimizeSet(dependencies) ); | |
195 new ModuleInfo(module,DEFAULT_ON.contains(name)); | |
196 } | |
197 } catch(IOException e) { | |
198 throw new RuntimeException(e); | |
199 } catch(CompileException e) { | |
200 throw new RuntimeException(e); | |
201 } | |
202 } | |
203 | |
204 | |
205 public static Module getModule(String moduleName) { | |
206 return MODULES.get(moduleName).module; | |
207 } | |
208 | |
209 public static boolean isEnabledByDefault(String moduleName) { | |
210 ModuleInfo info = MODULES.get(moduleName); | |
211 return info != null && info.isEnabledByDefault; | |
212 } | |
213 | |
214 | |
215 // from TemplateManager | |
216 | |
217 private static final Module SOURCE_MODULE; | |
218 | |
219 static { | |
220 List<Source> sources = new ArrayList<Source>(); | |
221 try { | |
222 for( URL url : listNamlFiles("/nabble/view/naml/") ) { | |
223 String s = url.toString(); | |
224 String name = s.substring(s.lastIndexOf('/')+1); | |
225 String content = IoUtils.read(url); | |
226 sources.add( Source.getInstance("nabble:"+name,content) ); | |
227 } | |
228 } catch(IOException e) { | |
229 throw new RuntimeException(e); | |
230 } | |
231 SOURCE_MODULE = new NamlModule( "nabble", sources, Collections.<String>emptySet() ); | |
232 logger.info("SOURCES has " + sources.size() + " macros"); | |
233 } | |
234 | |
235 | |
236 | |
237 public static boolean isConfigurationTweak(Source source) { | |
238 return source.id.equals(CONFIGURATION_TWEAK); | |
239 } | |
240 | |
241 public static boolean isCustomTweak(Source source) { | |
242 return source.id.startsWith(CUSTOM_TWEAK_PREFIX); | |
243 } | |
244 | |
245 public static List<Macro> getConfigurationMacros(Program program) throws CompileException { | |
246 List<Macro> macros = new ArrayList<Macro>(); | |
247 for( Source source : program.getSources() ) { | |
248 if( isConfigurationTweak(source) ) { | |
249 macros.addAll(source.getMacros()); | |
250 } | |
251 } | |
252 return macros; | |
253 } | |
254 | |
255 public static List<Macro> getCustomMacros(Program program) throws CompileException { | |
256 List<Macro> macros = new ArrayList<Macro>(); | |
257 for( Source source : program.getSources() ) { | |
258 if( isCustomTweak(source) ) { | |
259 macros.addAll(source.getMacros()); | |
260 } | |
261 } | |
262 return macros; | |
263 } | |
264 | |
265 public static void run(Template template,Writer out,Map<String,Object> args,Object... base) | |
266 throws IOException, ServletException | |
267 { | |
268 try { | |
269 template.run( new TemplatePrintWriter(out), args, base ); | |
270 } catch(TemplateRuntimeException e) { | |
271 Throwable cause = e.getCause(); | |
272 if( cause instanceof IOException ) | |
273 throw (IOException)cause; | |
274 if( cause instanceof ServletException ) | |
275 throw new ServletException(cause.getMessage(),e); | |
276 throw e; | |
277 } | |
278 } | |
279 | |
280 public static void nop() {} | |
281 | |
282 private ModuleManager() {} // never | |
283 } |