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 }