1. Stephen McKamey
  2. duel

Commits

Stephen McKamey  committed 3589b55

- adding attribute to CALL command which defers the call execution until client-side

  • Participants
  • Parent commits ecd453e
  • Branches default

Comments (0)

Files changed (5)

File src/org/duelengine/duel/ast/CALLCommandNode.java

View file
  • Ignore whitespace
 	public static final String INDEX = "index";
 	public static final String COUNT = "count";
 	public static final String KEY = "key";
+	public static final String DEFER = "defer";
+
 	private PARTCommandNode defaultPart;
+	private boolean defer;
 
 	public CALLCommandNode(int index, int line, int column) {
 		super(CMD, NAME, true, index, line, column);
 	public CALLCommandNode(AttributePair[] attr, DuelNode... children) {
 		super(CMD, NAME, true, attr, children);
 	}
+	
+	public void setDefer(boolean value) {
+		this.defer = value;
+	}
+
+	public boolean isDefer() {
+		return this.defer;
+	}
 
 	@Override
 	public boolean isSelf(String tag) {
 		if (name == null || name.length() == 0) {
 			throw new NullPointerException("name");
 		}
+		
+		if (name.equalsIgnoreCase(DEFER)) {
+			this.setDefer(true);
+			return;
+		}
+
 		if (!name.equalsIgnoreCase(VIEW) &&
 			!name.equalsIgnoreCase(DATA) &&
 			!name.equalsIgnoreCase(INDEX) &&
 		this.defaultPart.appendChild(child);
 	}
 
-	public static boolean isNullOrWhiteSpace(String value) {
+	private static boolean isNullOrWhiteSpace(String value) {
 		if (value == null) {
 			return true;
 		}

File src/org/duelengine/duel/codegen/ClientCodeGen.java

View file
  • Ignore whitespace
 			this.writeln(output, 0);
 
 			// prepend the client-side prefix
-			String viewName = this.settings.getFullName(view.getName());
+			String viewName = this.settings.getFullClientName(view.getName());
 			try {
 				if (this.encoder.writeNamespace(output, namespaces, viewName)) {
 					this.writeln(output, 0);
 			output.append(", {");
 			depth++;
 
-			boolean addPrefix = (node instanceof CALLCommandNode) && this.settings.hasNamePrefix();
+			boolean addPrefix = (node instanceof CALLCommandNode) && this.settings.hasClientNamePrefix();
 
 			boolean needsDelim = false;
 			for (String attr : attrs) {
 					if (addPrefix && "view".equalsIgnoreCase(attr) && attrVal instanceof ExpressionNode) {
 						// prepend the client-side prefix
 						ExpressionNode nameAttr = (ExpressionNode)attrVal; 
-						attrVal = new ExpressionNode(this.settings.getFullName(nameAttr.getValue()), nameAttr.getIndex(), nameAttr.getLine(), nameAttr.getColumn());
+						attrVal = new ExpressionNode(this.settings.getFullClientName(nameAttr.getValue()), nameAttr.getIndex(), nameAttr.getLine(), nameAttr.getColumn());
 					}
 					this.writeNode(output, attrVal, depth, preMode);
 				}

File src/org/duelengine/duel/codegen/CodeDOMBuilder.java

View file
  • Ignore whitespace
 	public CodeTypeDeclaration buildView(VIEWCommandNode viewNode) throws IOException {
 		try {
 			// prepend the server-side prefix
-			String fullName = this.settings.getFullName(viewNode.getName());
+			String fullName = this.settings.getFullServerName(viewNode.getName());
 			int lastDot = fullName.lastIndexOf('.');
 			String name = fullName.substring(lastDot+1);
 			String ns = (lastDot > 0) ? fullName.substring(0, lastDot) : null;
 	private void buildCall(CALLCommandNode node)
 		throws IOException {
 
+		if (node.isDefer()) {
+			this.buildDeferredCall(node);
+			return;
+		}
+		
 		// generate a field to hold the child template
 		CodeField field = new CodeField(
 				AccessModifierType.PRIVATE,
 		}
 
 		// prepend the server-side prefix
-		viewName = this.settings.getFullName(viewName);
+		viewName = this.settings.getFullServerName(viewName);
 
 		CodeExpression[] ctorArgs = new CodeExpression[node.getChildren().size()];
 
 					new CodeObjectCreateExpression(
 						viewName.trim(),
 						ctorArgs))));
-		
+
 		CodeExpression dataExpr;
 		DuelNode callData = node.getAttribute(CALLCommandNode.DATA);
 		if (callData instanceof CodeBlockNode) {
 			keyExpr));
 	}
 
+	private void buildDeferredCall(CALLCommandNode node)
+		throws IOException {
+
+		boolean prettyPrint = this.encoder.isPrettyPrint();
+		CodeStatementCollection scope = this.scopeStack.peek();
+
+		// use the script tag as its own replacement element
+		this.hasScripts = true;
+		this.formatter
+			.writeOpenElementBeginTag(this.buffer, "script")
+			.writeAttribute(this.buffer, "type", "text/javascript")
+			.writeOpenAttribute(this.buffer, "id");
+		CodeVariableDeclarationStatement idVar = this.emitClientID();
+		this.formatter
+			.writeCloseAttribute(this.buffer)
+			.writeCloseElementBeginTag(this.buffer);
+		this.ensureExternalsEmitted(false);
+
+		// emit patch function call which serializes attributes into object
+		this.buffer.append("duel.replace(");
+
+		// emit id var or known value
+		this.flushBuffer();
+		scope.add(new CodeMethodInvokeExpression(
+			Void.class,
+			new CodeThisReferenceExpression(),
+			"dataEncode",
+			new CodeVariableReferenceExpression(DuelContext.class, "context"),
+			new CodeVariableReferenceExpression(idVar),
+			CodePrimitiveExpression.ONE));
+		this.buffer.append(',');
+		if (prettyPrint) {
+			this.buffer.append(' ');
+		}
+
+		// determine the name of the template
+		String viewName = null;
+		DuelNode callView = node.getAttribute(CALLCommandNode.VIEW);
+		if (callView instanceof LiteralNode) {
+			viewName = ((LiteralNode)callView).getValue();
+		} else if (callView instanceof ExpressionNode) {
+			viewName = ((ExpressionNode)callView).getValue();
+		}
+
+		if (viewName != null) {
+			// prepend the server-side prefix
+			viewName = this.settings.getFullClientName(viewName);
+			this.buffer.append(viewName);
+
+		} else {
+			if (callView instanceof CodeBlockNode) {
+				CodeExpression viewExpr = this.translateExpression((CodeBlockNode)callView, false);
+
+				// emit view expression as a literal result of the expression
+				this.flushBuffer();
+				scope.add(new CodeMethodInvokeExpression(
+					Void.class,
+					new CodeThisReferenceExpression(),
+					"dataEncode",
+					new CodeVariableReferenceExpression(DuelContext.class, "context"),
+					viewExpr,
+					CodePrimitiveExpression.ONE));
+
+			} else {
+				// TODO: how to handle switcher method cases?
+				throw new InvalidNodeException("Unexpected Call command view attribute: "+callView, callView);
+			}
+		}
+
+		this.buffer.append(',');
+		if (prettyPrint) {
+			this.buffer.append(' ');
+		}
+		this.flushBuffer();
+
+		CodeExpression dataExpr;
+		DuelNode callData = node.getAttribute(CALLCommandNode.DATA);
+		if (callData instanceof CodeBlockNode) {
+			dataExpr = this.translateExpression((CodeBlockNode)callData, false);
+		} else {
+			dataExpr = new CodeVariableReferenceExpression(Object.class, "data");
+		}
+
+		// emit data expression as a literal
+		scope.add(new CodeMethodInvokeExpression(
+			Void.class,
+			new CodeThisReferenceExpression(),
+			"dataEncode",
+			new CodeVariableReferenceExpression(DuelContext.class, "context"),
+			dataExpr,
+			CodePrimitiveExpression.ONE));
+
+		this.buffer.append(',');
+		if (prettyPrint) {
+			this.buffer.append(' ');
+		}
+		this.flushBuffer();
+
+		CodeExpression indexExpr;
+		DuelNode callIndex = node.getAttribute(CALLCommandNode.INDEX);
+		if (callIndex instanceof CodeBlockNode) {
+			indexExpr = this.translateExpression((CodeBlockNode)callIndex, false);
+		} else {
+			indexExpr = new CodeVariableReferenceExpression(int.class, "index");
+		}
+
+		// emit index expression as a literal
+		scope.add(new CodeMethodInvokeExpression(
+			Void.class,
+			new CodeThisReferenceExpression(),
+			"dataEncode",
+			new CodeVariableReferenceExpression(DuelContext.class, "context"),
+			indexExpr,
+			CodePrimitiveExpression.ONE));
+
+		this.buffer.append(',');
+		if (prettyPrint) {
+			this.buffer.append(' ');
+		}
+		this.flushBuffer();
+
+		CodeExpression countExpr;
+		DuelNode callCount = node.getAttribute(CALLCommandNode.COUNT);
+		if (callCount instanceof CodeBlockNode) {
+			countExpr = this.translateExpression((CodeBlockNode)callCount, false);
+		} else {
+			countExpr = new CodeVariableReferenceExpression(int.class, "count");
+		}
+
+		// emit count expression as a literal
+		scope.add(new CodeMethodInvokeExpression(
+			Void.class,
+			new CodeThisReferenceExpression(),
+			"dataEncode",
+			new CodeVariableReferenceExpression(DuelContext.class, "context"),
+			countExpr,
+			CodePrimitiveExpression.ONE));
+
+		this.buffer.append(',');
+		if (prettyPrint) {
+			this.buffer.append(' ');
+		}
+		this.flushBuffer();
+
+		CodeExpression keyExpr;
+		DuelNode callKey = node.getAttribute(CALLCommandNode.KEY);
+		if (callKey instanceof CodeBlockNode) {
+			keyExpr = this.translateExpression((CodeBlockNode)callKey, false);
+		} else {
+			keyExpr = new CodeVariableReferenceExpression(String.class, "key");
+		}
+
+		// emit key expression as a literal
+		scope.add(new CodeMethodInvokeExpression(
+			Void.class,
+			new CodeThisReferenceExpression(),
+			"dataEncode",
+			new CodeVariableReferenceExpression(DuelContext.class, "context"),
+			keyExpr,
+			CodePrimitiveExpression.ONE));
+
+		this.buffer.append(");");
+
+		// last parameter will be the current data
+		this.formatter.writeElementEndTag(this.buffer, "script");
+	}
+
 	private CodeObjectCreateExpression buildPart(PARTCommandNode node)
 		throws IOException {
 

File src/org/duelengine/duel/codegen/CodeGenSettings.java

View file
  • Ignore whitespace
  */
 public class CodeGenSettings {
 
-	private String namePrefix;
+	private String clientPrefix;
+	private String serverPrefix;
 	private String indent = "\t";
 	private String newline = "\n";
 	private boolean convertLineEndings;
 	private boolean normalizeWhitespace;
 	private boolean encodeNonASCII = true;
 
-	public void setNamePrefix(String value) {
-		this.namePrefix = (value == null) ? null : value.trim();
+	public void setClientNamePrefix(String value) {
+		this.clientPrefix = (value == null) ? null : value.trim();
 	}
 
-	public String getNamePrefix() {
-		return this.namePrefix;
+	public String getClientNamePrefix() {
+		return this.clientPrefix;
 	}
 
-	public boolean hasNamePrefix() {
-		return (this.namePrefix != null) && (this.namePrefix.length() > 0);
+	public boolean hasClientNamePrefix() {
+		return (this.clientPrefix != null) && (this.clientPrefix.length() > 0);
 	}
 
-	public String getFullName(String name) {
-		if ((this.namePrefix == null) || (this.namePrefix.length() < 1)) {
+	public String getFullClientName(String name) {
+		if ((this.clientPrefix == null) || (this.clientPrefix.length() < 1)) {
 			return name;
 		}
 
 			name = name.trim();
 		}
 
-		return this.namePrefix+'.'+name;
+		return this.clientPrefix+'.'+name;
+	}
+
+	public void setServerNamePrefix(String value) {
+		this.serverPrefix = value;
+	}
+
+	public String getServerNamePrefix() {
+		return this.serverPrefix;
+	}
+
+	public boolean hasServerNamePrefix() {
+		return (this.serverPrefix != null) && (this.serverPrefix.length() > 0);
+	}
+
+	public String getFullServerName(String name) {
+		if ((this.serverPrefix == null) || (this.serverPrefix.length() < 1)) {
+			return name;
+		}
+
+		if (name != null) {
+			name = name.trim();
+		}
+
+		return this.serverPrefix+'.'+name;
 	}
 
 	/**

File src/org/duelengine/duel/compiler/DuelCompiler.java

View file
  • Ignore whitespace
 
 			// TODO: allow setting properties from args
 			CodeGenSettings settings = new CodeGenSettings();
+			settings.setIndent("\t");
 			settings.setNewline(System.getProperty("line.separator"));
+			settings.setClientNamePrefix(this.clientPrefix);
+			settings.setServerNamePrefix(this.serverPrefix);
 
 			// compact client-side
 			settings.setConvertLineEndings(false);
 			settings.setNormalizeWhitespace(true);
-			settings.setNamePrefix(this.clientPrefix);
 
 			CodeGenerator codegen = new ClientCodeGen(settings);
 			try {
 			// directly emit server-side
 			settings.setConvertLineEndings(true);
 			settings.setNormalizeWhitespace(false);
-			settings.setNamePrefix(this.serverPrefix);
 
 			codegen = new ServerCodeGen(settings);
 			for (VIEWCommandNode view : views) {
 				try {
-					File outputFile = new File(this.outputServerFolder, settings.getFullName(view.getName()).replace('.', '/')+codegen.getFileExtension());
+					File outputFile = new File(this.outputServerFolder, settings.getFullServerName(view.getName()).replace('.', '/')+codegen.getFileExtension());
 					outputFile.getParentFile().mkdirs();
 
 					FileWriter writer = new FileWriter(outputFile, false);