Mercurial Hosting > nabble
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/nabble/modules/ModuleManager.java Thu Mar 21 19:15:52 2019 -0600 @@ -0,0 +1,283 @@ +package nabble.modules; + +import fschmidt.util.java.CollectionUtils; +import fschmidt.util.java.IoUtils; +import nabble.model.Site; +import nabble.modules.hacks.HacksModule; +import nabble.modules.poll.PollModule; +import nabble.modules.workgroup.WorkgroupModule; +import nabble.naml.compiler.CompileException; +import nabble.naml.compiler.Macro; +import nabble.naml.compiler.Module; +import nabble.naml.compiler.Program; +import nabble.naml.compiler.Source; +import nabble.naml.compiler.StackTrace; +import nabble.naml.compiler.StackTraceElement; +import nabble.naml.compiler.Template; +import nabble.naml.compiler.TemplatePrintWriter; +import nabble.naml.compiler.TemplateRuntimeException; +import nabble.naml.dom.Attribute; +import nabble.naml.dom.Element; +import nabble.naml.dom.ElementName; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.ServletException; +import java.io.IOException; +import java.io.Writer; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + + +public final class ModuleManager { + private static final Logger logger = LoggerFactory.getLogger(ModuleManager.class); + + private static final Map<String,ModuleInfo> MODULES = new LinkedHashMap<String,ModuleInfo>(); + + private static class ModuleInfo { + final Module module; + final boolean isEnabledByDefault; + + ModuleInfo(Module module,boolean isEnabledByDefault) { + this.module = module; + this.isEnabledByDefault = isEnabledByDefault; + MODULES.put( module.getName(), this ); + } + } + + static { + new ModuleInfo(WorkgroupModule.INSTANCE,false); + new ModuleInfo(PollModule.INSTANCE,true); + new ModuleInfo(HacksModule.INSTANCE,false); + } + + private static final String CUSTOM_TWEAK_PREFIX = "custom_tweak:"; + public static final String CONFIGURATION_TWEAK = "configuration"; + + private static List<Module> getBaseModules() { + List<Module> modules = new ArrayList<Module>(); + modules.add(SOURCE_MODULE); + return modules; + } + + public static List<Module> getGenericModules() { + List<Module> modules = getBaseModules(); + for( ModuleInfo mi : MODULES.values() ) { + if( mi.isEnabledByDefault ) + modules.add( mi.module ); + } + modules = sort(modules); + return modules; + } + + public static List<Module> getModules(Site site) { + List<Module> modules = getBaseModules(); + for( ModuleInfo mi : MODULES.values() ) { + if( site.isModuleEnabled(mi.module.getName()) ) { + modules.add( mi.module ); + } + } + modules = sort(modules); + if( site.getTweakException() == null ) { + String config = site.getConfigurationTweak(); + if( config.length() > 0 ) { + Source source = Source.getInstance(CONFIGURATION_TWEAK,config); + Module module = new NamlModule( "config", Collections.singleton(source), Collections.<String>emptySet() ); + modules.add(module); + } + Map<String,String> tweaks = site.getCustomTweaks(); + if( !tweaks.isEmpty() ) { + List<Source> sources = new ArrayList<Source>(); + for( Map.Entry<String,String> entry : tweaks.entrySet() ) { + String name = entry.getKey(); + String content = entry.getValue(); + Source source = Source.getInstance(CUSTOM_TWEAK_PREFIX+name,content); + sources.add(source); + } + Module module = new NamlModule( "tweak", sources, Collections.<String>emptySet() ); + modules.add(module); + } + } + return modules; + } + + private static List<Module> sort(List<Module> modules) { + List<Module> rtn = new ArrayList<Module>(); + Set<String> names = new HashSet<String>(); + while( !modules.isEmpty() ) { + boolean changed = false; + for( Iterator<Module> iter = modules.iterator(); iter.hasNext(); ) { + Module m = iter.next(); + if( names.containsAll(m.getDependencies()) ) { + rtn.add(m); + names.add(m.getName()); + iter.remove(); + changed = true; + } + } + if( !changed ) + throw new RuntimeException("circular dependencies: "+modules); + } + return rtn; + } + + public static Collection<Source> loadSource(Module module) { + String moduleName = module.getName(); + try { + return Collections.singleton( + Source.getInstance( + moduleName + ":" + moduleName + ".naml", + IoUtils.read( ClassLoader.getSystemResource( + module.getClass().getPackage().getName().replace('.','/') + '/' + moduleName+".naml" + ) ) + ) + ); + } catch(IOException e) { + throw new RuntimeException(e); + } + } + + + private static List<URL> listNamlFiles(String path) { + List<URL> list = new ArrayList<URL>(); + for( URL url : IoUtils.getResources(ModuleManager.class) ) { + String s = url.toString(); + if( s.endsWith(".naml") && s.indexOf(path) != -1 ) + list.add(url); + } + return list; + } + + private static final ElementName DEPENDENCY = new ElementName("dependency"); + private static final Set<ElementName> IGNORE_TAGS = Collections.singleton(DEPENDENCY); + private static final Set<String> DEFAULT_ON = new HashSet<String>(); + static { + DEFAULT_ON.add("responsive"); + DEFAULT_ON.add("ads"); + } + + static { + try { + for( URL url : listNamlFiles("/nabble/modules/naml/") ) { + String s = url.toString(); + String name = s.substring(s.lastIndexOf('/')+1); + name = name.substring(0,name.length()-5); + Source source = Source.getInstance( name + ":" + name + ".naml", IoUtils.read(url), IGNORE_TAGS ); + Set<String> dependencies = new HashSet<String>(); + StackTrace stackTrace = new StackTrace(); + for( Object obj : source.parse() ) { + if( obj instanceof Element ) { + Element element = (Element)obj; + if( element.name().equals(DEPENDENCY) ) { + stackTrace.push( new StackTraceElement(source,element) ); + try { + Attribute attr = element.getAttribute("module"); + if( attr == null ) + throw new CompileException(stackTrace,"module attribute required"); + String dependency = attr.value().toString(); + dependencies.add(dependency); + } finally { + stackTrace.pop(); + } + } + } + } + Module module = new NamlModule( name, Collections.singleton(source), CollectionUtils.optimizeSet(dependencies) ); + new ModuleInfo(module,DEFAULT_ON.contains(name)); + } + } catch(IOException e) { + throw new RuntimeException(e); + } catch(CompileException e) { + throw new RuntimeException(e); + } + } + + + public static Module getModule(String moduleName) { + return MODULES.get(moduleName).module; + } + + public static boolean isEnabledByDefault(String moduleName) { + ModuleInfo info = MODULES.get(moduleName); + return info != null && info.isEnabledByDefault; + } + + + // from TemplateManager + + private static final Module SOURCE_MODULE; + + static { + List<Source> sources = new ArrayList<Source>(); + try { + for( URL url : listNamlFiles("/nabble/view/naml/") ) { + String s = url.toString(); + String name = s.substring(s.lastIndexOf('/')+1); + String content = IoUtils.read(url); + sources.add( Source.getInstance("nabble:"+name,content) ); + } + } catch(IOException e) { + throw new RuntimeException(e); + } + SOURCE_MODULE = new NamlModule( "nabble", sources, Collections.<String>emptySet() ); + logger.info("SOURCES has " + sources.size() + " macros"); + } + + + + public static boolean isConfigurationTweak(Source source) { + return source.id.equals(CONFIGURATION_TWEAK); + } + + public static boolean isCustomTweak(Source source) { + return source.id.startsWith(CUSTOM_TWEAK_PREFIX); + } + + public static List<Macro> getConfigurationMacros(Program program) throws CompileException { + List<Macro> macros = new ArrayList<Macro>(); + for( Source source : program.getSources() ) { + if( isConfigurationTweak(source) ) { + macros.addAll(source.getMacros()); + } + } + return macros; + } + + public static List<Macro> getCustomMacros(Program program) throws CompileException { + List<Macro> macros = new ArrayList<Macro>(); + for( Source source : program.getSources() ) { + if( isCustomTweak(source) ) { + macros.addAll(source.getMacros()); + } + } + return macros; + } + + public static void run(Template template,Writer out,Map<String,Object> args,Object... base) + throws IOException, ServletException + { + try { + template.run( new TemplatePrintWriter(out), args, base ); + } catch(TemplateRuntimeException e) { + Throwable cause = e.getCause(); + if( cause instanceof IOException ) + throw (IOException)cause; + if( cause instanceof ServletException ) + throw new ServletException(cause.getMessage(),e); + throw e; + } + } + + public static void nop() {} + + private ModuleManager() {} // never +}