view src/nabble/modules/ModuleManager.java @ 24:e0c501fb5229

remove trk
author Franklin Schmidt <fschmidt@gmail.com>
date Sun, 14 Jun 2020 17:56:59 -0600
parents 7ecd1a4ef557
children
line wrap: on
line source

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");
	}

	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
}