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