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 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 }
|