view src/nabble/naml/compiler/CommandSpec.java @ 0:7ecd1a4ef557

add content
author Franklin Schmidt <fschmidt@gmail.com>
date Thu, 21 Mar 2019 19:15:52 -0600
parents
children
line wrap: on
line source

package nabble.naml.compiler;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;


public final class CommandSpec {

	public static CommandSpec.Builder DO() {
		return new CommandSpec.Builder()
			.scopedParameters("do")
			.dotParameter("do")
			.outputtedParameters("do")
			.optionalParameters("do")
		;
	}

	public static final CommandSpec DO = DO().build();

	public static CommandSpec.Builder TEXT() {
		return new CommandSpec.Builder()
			.dotParameter("text")
		;
	}

	public static final CommandSpec TEXT = TEXT().build();

	public static final CommandSpec OPTIONAL_TEXT = TEXT()
		.optionalParameters("text")
		.build()
	;

	public final static CommandSpec EMPTY = new Builder().build();

	public static CommandSpec.Builder NO_OUTPUT() {
		return new CommandSpec.Builder()
			.outputtedParameters()
		;
	}

	public static final CommandSpec NO_OUTPUT = NO_OUTPUT().build();

	final Set<String> parameters;
	final Set<String> scopedParameters;
	final String dotParameter;
	final Set<String> requiredParameters;
	private final Map<String,Set<String>> restrictedParams;
	final boolean removeNulls;
	final Set<String> outputtedParameters;
	final Set<Class> requiredInStack;

	public static class Builder {
		private final Set<String> parameters = new HashSet<String>();
		private Set<String> scopedParameters = null;
		private String dotParameter = null;
		private Set<String> optionalParameters = null;
		private Map<String,Set<String>> restrictedParams = null;
		private boolean removeNulls = true;
		private Set<String> outputtedParameters = null;
		private final Set<Class> requiredInStack = new HashSet<Class>();

		public Builder parameters(String... parameters) {
			this.parameters.addAll( Arrays.asList(parameters) );
			return this;
		}

		public Builder scopedParameters(String... params) {
			this.scopedParameters = new HashSet<String>( Arrays.asList(params) );
			return this;
		}

		public Builder dotParameter(String param) {
			this.dotParameter = param;
			return this;
		}

		public Builder optionalParameters(String... params) {
			if( optionalParameters == null )
				optionalParameters = new HashSet<String>();
			optionalParameters.addAll( Arrays.asList(params) );
			return this;
		}

		public Builder restrictedParameter(String param,String... options) {
			if( restrictedParams==null )
				restrictedParams = new HashMap<String,Set<String>>();
			restrictedParams.put( param, new HashSet<String>(Arrays.asList(options)) );
			return this;
		}

		public Builder dontRemoveNulls() {
			removeNulls = false;
			return this;
		}

		public Builder outputtedParameters(String... params) {
			if( outputtedParameters == null )
				outputtedParameters = new HashSet<String>();
			outputtedParameters.addAll( Arrays.asList(params) );
			return this;
		}

		public Builder requiredInStack(Class... requiredInStack) {
			this.requiredInStack.addAll( Arrays.asList(requiredInStack) );
			return this;
		}

		public CommandSpec build() {
			return new CommandSpec(this);
		}
	}

	private CommandSpec(Builder builder) {
		parameters = builder.parameters;
		scopedParameters = builder.scopedParameters;
		dotParameter = builder.dotParameter;
		restrictedParams = builder.restrictedParams;
		outputtedParameters = builder.outputtedParameters;
		requiredInStack = builder.requiredInStack.isEmpty() ? Collections.<Class>emptySet() : builder.requiredInStack;
		if( scopedParameters != null )
			parameters.addAll(scopedParameters);
		if( dotParameter != null )
			parameters.add(dotParameter);
		if( restrictedParams != null )
			parameters.addAll(restrictedParams.keySet());
		if( outputtedParameters != null )
			parameters.addAll(outputtedParameters);
		requiredParameters = new HashSet<String>(parameters);
		if( builder.optionalParameters != null ) {
			parameters.addAll(builder.optionalParameters);
			requiredParameters.removeAll(builder.optionalParameters);
		}
		removeNulls = builder.removeNulls;
		for( String s : parameters ) {
			if( s.indexOf('-') != -1 )
				throw new RuntimeException("macro attibute '"+s+"' may not contain '-'");
		}
	}

	boolean hasScopedParam(String param) {
		return scopedParameters!=null && scopedParameters.contains(param);
	}

	Set<String> getParameters() {
		return Collections.unmodifiableSet(parameters);
	}

	boolean hasParameter(String param) {
		return parameters.contains(param);
	}

	static final String dotWarning = "you may have forgotten the dot at the end of the enclosing tag name";

	void check(Map<String,String> staticArgs,Map<String,Chunk> dynamicArgs,StackTrace stackTrace)
		throws CompileException
	{
		Set<String> params = new HashSet<String>();
		params.addAll(staticArgs.keySet());
		params.addAll(dynamicArgs.keySet());
		if( !parameters.containsAll(params) ) {
			params.removeAll(parameters);
			String msg = "parameter(s) "+params+" not allowed, only use "+parameters;
			if( dotParameter != null )
				msg += " or "+dotWarning;
			throw new CompileException(stackTrace,msg);
		}
		if( !params.containsAll(requiredParameters) ) {
			Set<String> required = new HashSet<String>(requiredParameters);
			required.removeAll(params);
			if( required.size()==1 && required.iterator().next().equals(dotParameter) )
				throw new CompileMethodException(stackTrace,"dot_parameter "+required+" was not found but is required, "+dotWarning);
			throw new CompileMethodException(stackTrace,"parameter(s) "+required+" were not found but are required");
		}
		if( restrictedParams!=null ) {
			for( Map.Entry<String,Set<String>> entry : restrictedParams.entrySet() ) {
				String name = entry.getKey();
				Set<String> options = entry.getValue();
				Chunk chunk = dynamicArgs.get(name);
				if( chunk!=null && !(chunk==Chunk.NULL && removeNulls) )
					throw new CompileException(stackTrace,"parameter '"+name+"' cannot be dynamic");
				String value = staticArgs.get(name);
				if( value != null && !options.contains(value) )
					throw new CompileException(stackTrace,"parameter '"+name+"' cannot be '"+value+"', must be one of "+options);
			}
		}
	}

}