0
|
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 }
|
|
166
|
|
167 static {
|
|
168 try {
|
|
169 for( URL url : listNamlFiles("/nabble/modules/naml/") ) {
|
|
170 String s = url.toString();
|
|
171 String name = s.substring(s.lastIndexOf('/')+1);
|
|
172 name = name.substring(0,name.length()-5);
|
|
173 Source source = Source.getInstance( name + ":" + name + ".naml", IoUtils.read(url), IGNORE_TAGS );
|
|
174 Set<String> dependencies = new HashSet<String>();
|
|
175 StackTrace stackTrace = new StackTrace();
|
|
176 for( Object obj : source.parse() ) {
|
|
177 if( obj instanceof Element ) {
|
|
178 Element element = (Element)obj;
|
|
179 if( element.name().equals(DEPENDENCY) ) {
|
|
180 stackTrace.push( new StackTraceElement(source,element) );
|
|
181 try {
|
|
182 Attribute attr = element.getAttribute("module");
|
|
183 if( attr == null )
|
|
184 throw new CompileException(stackTrace,"module attribute required");
|
|
185 String dependency = attr.value().toString();
|
|
186 dependencies.add(dependency);
|
|
187 } finally {
|
|
188 stackTrace.pop();
|
|
189 }
|
|
190 }
|
|
191 }
|
|
192 }
|
|
193 Module module = new NamlModule( name, Collections.singleton(source), CollectionUtils.optimizeSet(dependencies) );
|
|
194 new ModuleInfo(module,DEFAULT_ON.contains(name));
|
|
195 }
|
|
196 } catch(IOException e) {
|
|
197 throw new RuntimeException(e);
|
|
198 } catch(CompileException e) {
|
|
199 throw new RuntimeException(e);
|
|
200 }
|
|
201 }
|
|
202
|
|
203
|
|
204 public static Module getModule(String moduleName) {
|
|
205 return MODULES.get(moduleName).module;
|
|
206 }
|
|
207
|
|
208 public static boolean isEnabledByDefault(String moduleName) {
|
|
209 ModuleInfo info = MODULES.get(moduleName);
|
|
210 return info != null && info.isEnabledByDefault;
|
|
211 }
|
|
212
|
|
213
|
|
214 // from TemplateManager
|
|
215
|
|
216 private static final Module SOURCE_MODULE;
|
|
217
|
|
218 static {
|
|
219 List<Source> sources = new ArrayList<Source>();
|
|
220 try {
|
|
221 for( URL url : listNamlFiles("/nabble/view/naml/") ) {
|
|
222 String s = url.toString();
|
|
223 String name = s.substring(s.lastIndexOf('/')+1);
|
|
224 String content = IoUtils.read(url);
|
|
225 sources.add( Source.getInstance("nabble:"+name,content) );
|
|
226 }
|
|
227 } catch(IOException e) {
|
|
228 throw new RuntimeException(e);
|
|
229 }
|
|
230 SOURCE_MODULE = new NamlModule( "nabble", sources, Collections.<String>emptySet() );
|
|
231 logger.info("SOURCES has " + sources.size() + " macros");
|
|
232 }
|
|
233
|
|
234
|
|
235
|
|
236 public static boolean isConfigurationTweak(Source source) {
|
|
237 return source.id.equals(CONFIGURATION_TWEAK);
|
|
238 }
|
|
239
|
|
240 public static boolean isCustomTweak(Source source) {
|
|
241 return source.id.startsWith(CUSTOM_TWEAK_PREFIX);
|
|
242 }
|
|
243
|
|
244 public static List<Macro> getConfigurationMacros(Program program) throws CompileException {
|
|
245 List<Macro> macros = new ArrayList<Macro>();
|
|
246 for( Source source : program.getSources() ) {
|
|
247 if( isConfigurationTweak(source) ) {
|
|
248 macros.addAll(source.getMacros());
|
|
249 }
|
|
250 }
|
|
251 return macros;
|
|
252 }
|
|
253
|
|
254 public static List<Macro> getCustomMacros(Program program) throws CompileException {
|
|
255 List<Macro> macros = new ArrayList<Macro>();
|
|
256 for( Source source : program.getSources() ) {
|
|
257 if( isCustomTweak(source) ) {
|
|
258 macros.addAll(source.getMacros());
|
|
259 }
|
|
260 }
|
|
261 return macros;
|
|
262 }
|
|
263
|
|
264 public static void run(Template template,Writer out,Map<String,Object> args,Object... base)
|
|
265 throws IOException, ServletException
|
|
266 {
|
|
267 try {
|
|
268 template.run( new TemplatePrintWriter(out), args, base );
|
|
269 } catch(TemplateRuntimeException e) {
|
|
270 Throwable cause = e.getCause();
|
|
271 if( cause instanceof IOException )
|
|
272 throw (IOException)cause;
|
|
273 if( cause instanceof ServletException )
|
|
274 throw new ServletException(cause.getMessage(),e);
|
|
275 throw e;
|
|
276 }
|
|
277 }
|
|
278
|
|
279 public static void nop() {}
|
|
280
|
|
281 private ModuleManager() {} // never
|
|
282 }
|