/*
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package flex2.compiler.mxml.builder;
import flex2.compiler.CompilationUnit;
import flex2.compiler.util.CompilerMessage.CompilerError;
import flex2.compiler.util.CompilerMessage.CompilerWarning;
import flex2.compiler.SymbolTable;
import flex2.compiler.mxml.MxmlConfiguration;
import flex2.compiler.mxml.dom.CDATANode;
import flex2.compiler.mxml.dom.Node;
import flex2.compiler.mxml.reflect.TypeTable;
import flex2.compiler.mxml.reflect.Type;
import flex2.compiler.mxml.rep.*;
import flex2.compiler.util.NameFormatter;
import flex2.compiler.util.QName;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import macromedia.asc.util.IntegerPool;
/*
* TODO port to new setup
* TODO the primitive-with-properties thing doesn't work in AVM+. Blocked in codegen but must error here also.
*/
/**
* This builder contains code common to ModelBuilder and
* ServiceRequestBuilder.
*
* @author Matt Chotin
*/
abstract class AnonymousObjectGraphBuilder extends AbstractBuilder
{
AnonymousObjectGraph graph;
/* Are nodes allow to have two-way inline bind expressions? */
private boolean allowTwoWayBind = true;
AnonymousObjectGraphBuilder(CompilationUnit unit, TypeTable typeTable, MxmlConfiguration mxmlConfiguration, MxmlDocument document)
{
super(unit, typeTable, mxmlConfiguration, document);
}
private Object processNode(Node node)
{
if (node.getChildCount() == 1 && node.getChildAt(0) instanceof CDATANode)
{
CDATANode cdata = (CDATANode)node.getChildAt(0);
if (cdata.image.length() > 0)
{
Object value = textParser.parseValue(cdata.image, typeTable.objectType, 0,
cdata.beginLine, NameFormatter.toDot(graph.getType().getName()));
if (value instanceof BindingExpression)
{
BindingExpression be = (BindingExpression) value;
if (!allowTwoWayBind && be.isTwoWayPrimary())
{
log(cdata, new TwoWayBindingNotAllowed());
return null;
}
else if (hasAttributeInitializers(node))
{
log(cdata, new HasAttributeInitializers());
return null;
}
else
{
return value;
}
}
else
{
boolean isPrimitive = (value instanceof String) || (value instanceof Number) || (value instanceof Boolean);
if (node.getAttributeCount() > 0 && isPrimitive)
{
//turn it into a Primitive with properties
Primitive p = new Primitive(document, getPrimitiveType(typeTable, value), value, cdata.beginLine);
for (Iterator i = node.getAttributeNames(); i.hasNext();)
{
QName propName = (QName)i.next();
p.setProperty(propName.getLocalPart(), node.getAttributeValue(propName), node.getLineNumber(propName));
}
value = p;
}
return value;
}
}
else
{
// do nothing if the cdata node has nothing...
return null;
}
}
else
{
Model model = new Model(document, graph.getType(), node.beginLine);
model.setId(node.getLocalPart(), false);
model.setIsAnonymous(true);
processAttributes(node, model);
processChildren(node, model);
return model;
}
}
protected void processChildren(Node node, Model parent)
{
Map<String, Array> arrays = createArrays(parent, countChildren(node));
for (int i = 0, count = node.getChildCount(); i < count; i++)
{
Node child = (Node)node.getChildAt(i);
if (child instanceof CDATANode)
{
// C: ignore CDATANode if other XML elements exist...
log(child, new IgnoringCDATA(child.image));
continue;
}
String namespaceURI = child.getNamespace();
String localPart = child.getLocalPart();
// C: move this check to Grammar.jj or SyntaxAnalyzer
if (SymbolTable.OBJECT.equals(localPart) && namespaceURI.length() == 0)
{
log(child, new ObjectTag());
continue;
}
Object value = processNode(child);
if (value == null)
{
// continue;
}
else if (arrays.containsKey(localPart))
{
Array arrayVal = arrays.get(localPart);
if (value instanceof BindingExpression)
{
BindingExpression bexpr = (BindingExpression)value;
bexpr.setDestination(arrayVal);
bexpr.setDestinationProperty(arrayVal.size());
bexpr.setDestinationLValue(Integer.toString(arrayVal.size()));
}
else if (value instanceof Model)
{
Model valueModel = (Model)value;
valueModel.setParent(arrayVal);
valueModel.setParentIndex(arrayVal.size());
}
arrayVal.addEntry(value, child.beginLine);
}
else
{
if (value instanceof BindingExpression)
{
BindingExpression bexpr = (BindingExpression)value;
bexpr.setDestination(parent);
bexpr.setDestinationProperty(localPart);
bexpr.setDestinationLValue(localPart);
}
else if (value instanceof Model)
{
Model valueModel = (Model)value;
valueModel.setParent(parent);
}
parent.setProperty(localPart, value, child.beginLine);
}
}
for (int i = 0, count = node.getChildCount(); i < count; i++)
{
Node child = (Node)node.getChildAt(i);
if (child instanceof CDATANode)
{
continue;
}
String namespaceURI = child.getNamespace();
String localPart = child.getLocalPart();
if (SymbolTable.OBJECT.equals(localPart) && namespaceURI.length() == 0)
{
continue;
}
if (arrays.containsKey(localPart))
{
Array arrayVal = arrays.get(localPart);
parent.setProperty(localPart, arrayVal);
}
}
}
private void processAttributes(Node node, Model model)
{
for (Iterator i = node.getAttributeNames(); i != null && i.hasNext();)
{
QName qname = (QName)i.next();
String text = (String)node.getAttributeValue(qname);
String localPart = qname.getLocalPart();
int line = node.getLineNumber(qname);
processDynamicPropertyText(localPart, text, AbstractBuilder.TextOrigin.FROM_ATTRIBUTE, line, model, null);
}
}
private Map<String, Integer> countChildren(Node node)
{
Map<String, Integer> counts = new HashMap<String, Integer>();
for (Iterator i = node.getAttributeNames(); i != null && i.hasNext();)
{
QName qname = (QName)i.next();
String namespaceURI = qname.getNamespace();
String localPart = qname.getLocalPart();
if (SymbolTable.OBJECT.equals(localPart) && namespaceURI.length() == 0)
{
continue;
}
if (!counts.containsKey(localPart))
{
counts.put(localPart, IntegerPool.getNumber(1));
}
else
{
int count = counts.get(localPart).intValue() + 1;
counts.put(localPart, IntegerPool.getNumber(count));
}
}
for (int i = 0, count = node.getChildCount(); i < count; i++)
{
Node child = (Node)node.getChildAt(i);
if (child instanceof CDATANode)
{
continue;
}
String namespaceURI = child.getNamespace();
String localPart = child.getLocalPart();
if (SymbolTable.OBJECT.equals(localPart) && namespaceURI.length() == 0)
{
continue;
}
if (!counts.containsKey(localPart))
{
counts.put(localPart, IntegerPool.getNumber(1));
}
else
{
int num = counts.get(localPart).intValue() + 1;
counts.put(localPart, IntegerPool.getNumber(num));
}
}
return counts;
}
private Map<String, Array> createArrays(Model parent, Map<String, Integer> counts)
{
Map<String, Array> arrays = new HashMap<String, Array>();
for (Iterator<String> i = counts.keySet().iterator(); i.hasNext();)
{
String localPart = i.next();
if (counts.get(localPart).intValue() > 1)
{
Array a = new Array(document, parent, parent.getXmlLineNumber(), typeTable.objectType);
a.setId(localPart, false);
a.setIsAnonymous(true);
// prepopulate with any properties definied as attributes
if (parent.hasProperty(localPart))
{
a.addEntry(parent.getProperty(localPart), parent.getXmlLineNumber());
}
arrays.put(localPart, a);
}
}
return arrays;
}
/**
* map some java types into AS
* TODO should go away once we use Primitive universally
*/
private static Type getPrimitiveType(TypeTable typeTable, Object o)
{
return (o instanceof Boolean) ? typeTable.booleanType :
(o instanceof Number) ? typeTable.numberType :
typeTable.stringType;
}
public static class HasAttributeInitializers extends CompilerError
{
private static final long serialVersionUID = 2742129880173923426L;
}
public static class IgnoringCDATA extends CompilerWarning
{
private static final long serialVersionUID = -1934672020085831809L;
public String image;
public IgnoringCDATA(String image)
{
this.image = image;
}
}
public static class ObjectTag extends CompilerError
{
private static final long serialVersionUID = 5342534491716831705L;
}
protected void setAllowTwoWayBind(boolean allowTwoWayBind)
{
this.allowTwoWayBind = allowTwoWayBind;
}
}