1 /*
2     Copyright (c) 2008-2009 NetAllied Systems GmbH
3 
4     This file is part of COLLADASaxFrameworkLoader.
5 
6     Licensed under the MIT Open Source License,
7     for details please see LICENSE file or the website
8     http://www.opensource.org/licenses/mit-license.php
9 */
10 
11 #include "COLLADASaxFWLStableHeaders.h"
12 #include "COLLADASaxFWLFormulasLinker.h"
13 #include "COLLADASaxFWLSidAddress.h"
14 #include "COLLADASaxFWLSidTreeNode.h"
15 #include "COLLADASaxFWLCOLLADACsymbol.h"
16 #include "COLLADASaxFWLFileLoader.h"
17 
18 #include "MathMLASTNode.h"
19 #include "MathMLASTArithmeticExpression.h"
20 #include "MathMLASTBinaryComparisionExpression.h"
21 #include "MathMLASTConstantExpression.h"
22 #include "MathMLASTFragmentExpression.h"
23 #include "MathMLASTFunctionExpression.h"
24 #include "MathMLASTLogicExpression.h"
25 #include "MathMLASTUnaryArithmeticExpression.h"
26 #include "MathMLASTVariableExpression.h"
27 
28 namespace COLLADASaxFWL
29 {
30 
31     //------------------------------
FormulasLinker(DocumentProcessor * documentProcessor,COLLADAFW::FormulaArray & formulas)32 	FormulasLinker::FormulasLinker( DocumentProcessor* documentProcessor, COLLADAFW::FormulaArray& formulas )
33 		: mDocumentProcessor(documentProcessor)
34 		, mFormulas(formulas)
35 	{
36 	}
37 
38     //------------------------------
~FormulasLinker()39 	FormulasLinker::~FormulasLinker()
40 	{
41 	}
42 
43 	//------------------------------
link()44 	bool FormulasLinker::link()
45 	{
46 		size_t formulasCount = mFormulas.getCount();
47 		for ( size_t i = 0; i < formulasCount; ++i)
48 		{
49 			COLLADAFW::Formula* formula = mFormulas[i];
50 			COLLADAFW::MathmlAstArray& asts = formula->getMathmlAsts();
51 			size_t astsCount = asts.getCount();
52 			for ( size_t j = 0; j < astsCount; ++j)
53 			{
54 				MathML::AST::INode*& astNode =  asts[j];
55 				bool success = true;
56 				astNode = link( formula, astNode, success);
57 				if ( !success )
58 					return false;
59 			}
60 		}
61 		return true;
62 	}
63 
64 	//------------------------------
link(COLLADAFW::Formula * formula,MathML::AST::INode * astNode,bool & success)65 	MathML::AST::INode* FormulasLinker::link( COLLADAFW::Formula* formula, MathML::AST::INode* astNode, bool& success)
66 	{
67 		switch ( astNode->getNodeType() )
68 		{
69 		case MathML::AST::INode::ARITHMETIC:
70 			{
71 				MathML::AST::ArithmeticExpression* arithmetic = (MathML::AST::ArithmeticExpression*)astNode;
72 				MathML::AST::NodeList& operands = arithmetic->getOperands();
73 				for ( size_t i = 0, count = operands.size(); i < count; ++i )
74 				{
75 					operands[i] = link(formula, operands[i], success);
76 				}
77 			}
78 			return astNode;
79 		case MathML::AST::INode::COMPARISON:
80 			{
81 				MathML::AST::BinaryComparisonExpression* comparison = (MathML::AST::BinaryComparisonExpression*)astNode;
82 				comparison->setLeftOperand(link(formula, comparison->getLeftOperand(), success));
83 				comparison->setRightOperand(link(formula, comparison->getRightOperand(), success));
84 			}
85 			return astNode;
86 		case MathML::AST::INode::CONSTANT:
87 		case MathML::AST::INode::VARIABLE:
88 			success = true;
89 			return astNode;
90 		case MathML::AST::INode::FRAGMENT:
91 			{
92 				MathML::AST::FragmentExpression* fragment = (MathML::AST::FragmentExpression*)astNode;
93 				fragment->setFragment( link( formula, fragment->getFragment(), success) );
94 			}
95 			return astNode;
96 		case MathML::AST::INode::LOGICAL:
97 			{
98 				MathML::AST::LogicExpression* logical = (MathML::AST::LogicExpression*)astNode;
99 				MathML::AST::NodeList& operands = logical->getOperands();
100 				for ( size_t i = 0, count = operands.size(); i < count; ++i )
101 				{
102 					operands[i] = link(formula, operands[i], success);
103 				}
104 			}
105 			return astNode;
106 		case MathML::AST::INode::UNARY:
107 			{
108 				MathML::AST::UnaryExpression* unary = (MathML::AST::UnaryExpression*)astNode;
109 				unary->setOperand(link(formula, unary->getOperand(), success));
110 			}
111 			return astNode;
112 		case MathML::AST::INode::FUNCTION:
113 			{
114 				MathML::AST::FunctionExpression* func = (MathML::AST::FunctionExpression*)astNode;
115 				MathML::AST::NodeList& operands = func->getParameterList();
116 				for ( size_t i = 0, count = operands.size(); i < count; ++i )
117 				{
118 					operands[i] = link(formula, operands[i], success);
119 				}
120 			}
121 			return astNode;
122 		case MathML::AST::INode::USERDEFINED:
123 			{
124 				COLLADACsymbol* csymbol = (COLLADACsymbol*) astNode;
125 				MathML::AST::INode* linkedNode = link( formula, csymbol, success );
126 				delete csymbol;
127 				return linkedNode;
128 			}
129 		default:
130 			COLLADABU_ASSERT(false);
131 		}
132 		return 0;
133 	}
134 
135 	//------------------------------
link(COLLADAFW::Formula * formula,COLLADACsymbol * csymbol,bool & success)136 	MathML::AST::INode* FormulasLinker::link( COLLADAFW::Formula* formula, COLLADACsymbol* csymbol, bool& success)
137 	{
138 		const SidAddress& targetAddress = csymbol->getSidAddress();
139 
140 		const String* csymbolName = 0;
141 		// if the address consist of a single word, this is considered to be the csymbol name.
142 		if ( !targetAddress.getId().empty() &&
143 			targetAddress.getSids().empty() &&
144 			targetAddress.getMemberSelection() == SidAddress::MEMBER_SELECTION_NONE )
145 		{
146 			csymbolName = &targetAddress.getId();
147 		}
148 
149 		if ( csymbol->getCSymbolType() == COLLADACsymbol::FUNCTION )
150 		{
151 			const COLLADAFW::UniqueId& formulaUniqueId = csymbol->getFormulaUniqueId();
152 
153 			COLLADAFW::Formula* targetFormula = mDocumentProcessor->getFormulaByUniqueId(formulaUniqueId);
154 
155 			MathML::AST::FragmentExpression* fragmentExpression = 0;
156 
157 			if ( targetFormula )
158 			{
159 				// find a suitable name for the fragment expression
160 				// if possible use the string in the csymbol element, otherwise the name of the referenced fromula
161 				const String* fragmentExpressionName = 0;
162 				fragmentExpressionName = csymbolName ? csymbolName : &targetFormula->getName();
163 
164 				fragmentExpression = new MathML::AST::FragmentExpression(*fragmentExpressionName, MathML::AST::INode::CLONEFLAG_DEEPCOPY_FRAGMENT_PARAMS);
165 				COLLADAFW::MathmlAstArray& asts = targetFormula->getMathmlAsts();
166 				if ( asts.getCount() == 1)
167 				{
168 					// we have to ensure, that the fragment won't change anymore. Therefore we need to link immediately
169 					bool fragmentSuccess = true;
170 					asts[0] = link( targetFormula, asts[0], fragmentSuccess);
171 					fragmentExpression->setFragment( asts[0] );
172 					if ( !fragmentSuccess )
173 					{
174 						return 0;
175 					}
176 				}
177 				const COLLADACsymbol::ParameterList& parameters =  csymbol->getParameterList();
178 				const COLLADAFW::FormulaNewParamPointerArray& targetFormulaParameters = targetFormula->getNewParams();
179 				if ( parameters.size() != targetFormulaParameters.getCount() )
180 				{
181 					String msg = "Number of newparams in formula \"" + *fragmentExpressionName;
182 					const String& formulaName = formula->getName();
183 					if ( !formulaName.empty() )
184 					{
185 						msg.append( "referenced in \"" + formulaName + "\" ");
186 					}
187 					msg.append( "does not match parameters count." );
188 
189 					success = mDocumentProcessor->handleFWLError(SaxFWLError::ERROR_PARAMETER_COUNT_DOESNOT_MATCH, msg);
190 				}
191 				else
192 				{
193 					COLLADACsymbol::ParameterList::const_iterator it = parameters.begin();
194 					for ( size_t i = 0; it!=parameters.end(); ++it, ++i )
195 					{
196 						const COLLADAFW::FormulaNewParam* parameter = targetFormulaParameters[i];
197 						fragmentExpression->addParameter(parameter->getName(), link( formula, *it, success));
198 						if ( !success )
199 						{
200 							break;
201 						}
202 					}
203 				}
204 			}
205 			else
206 			{
207 				// formula not found
208 				fragmentExpression = new MathML::AST::FragmentExpression(targetAddress.getSidAddressString(), MathML::AST::INode::CLONEFLAG_DEEPCOPY_FRAGMENT_PARAMS);
209 
210 				String msg = "Formula with unique id\"" + formulaUniqueId.toAscii() + "\" ";
211 				msg.append( "could not be found." );
212 
213 				success = mDocumentProcessor->handleFWLError(SaxFWLError::ERROR_UNRESOLVED_FORMULA, msg);
214 			}
215 			return fragmentExpression;
216 		}
217 		else
218 		{
219 			MathML::AST::VariableExpression* variableExpression = 0;
220 
221 			// the csymbol is a parameter.
222 			// if the address consist of a single word, it might be a parameter of the formula itself.
223 			if ( csymbolName )
224 			{
225 				//search for the parameter name in the formulas new params
226 				bool found = false;
227 				getNewParamIndex(formula, *csymbolName, found);
228 				if ( found )
229 				{
230 					// we are done. create new VariableExpression and return
231 					variableExpression = FW_NEW MathML::AST::VariableExpression(*csymbolName);
232 					success = true;
233 					return variableExpression;
234 				}
235 			}
236 
237 			// Try to find the referenced parameter in the entire COLLADA file, using ordinary sid address
238 			// syntax
239 			const SidTreeNode* targetSidTreeNode = mDocumentProcessor->resolveSid( targetAddress );
240 
241 			if ( !targetSidTreeNode )
242 			{
243 				// parameter not found
244 				String msg = "Parameter with sid address \"" + targetAddress.getSidAddressString() + "\" ";
245 				const String& formulaName = formula->getName();
246 				if ( !formulaName.empty() )
247 				{
248 					msg.append( "referenced in \"" + formulaName + "\" ");
249 				}
250 				msg.append( "could not be resolved." );
251 
252 				success = mDocumentProcessor->handleFWLError(SaxFWLError::ERROR_UNRESOLVED_PARAMETER, msg);
253 			}
254 			else
255 			{
256 				// parameter found
257 				if (targetSidTreeNode->getTargetType() == SidTreeNode::TARGETTYPECLASS_OBJECT)
258 				{
259 					// the parameter is an object, e.g. a joint
260 					const COLLADAFW::Object* object = targetSidTreeNode->getObjectTarget();
261 					COLLADABU_ASSERT(object);
262 					variableExpression = new MathML::AST::VariableExpression(object->getUniqueId().toAscii());
263 				}
264 				else if (targetSidTreeNode->getTargetType() == SidTreeNode::TARGETTYPECLASS_INTERMEDIATETARGETABLE)
265 				{
266 					// the parameter is an object, e.g. a joint
267 					const IntermediateTargetable* intermediateTargetable = targetSidTreeNode->getIntermediateTargetableTarget();
268 					COLLADABU_ASSERT(intermediateTargetable);
269 					const KinematicInstance* kinematicInstance = intermediateTargetableSafeCast<KinematicInstance>(intermediateTargetable);
270 					// for now we only support kinematic instances here
271 					COLLADABU_ASSERT(kinematicInstance);
272 					variableExpression = new MathML::AST::VariableExpression(kinematicInstance->getReplacingObjectUniqueId().toAscii());
273 				}
274 				else
275 				{
276 					// TODO: animatabel not supported yet
277 					success = false;
278 					return 0;
279 				}
280 
281 			}
282 			success = true;
283 			return variableExpression;
284 		}
285 
286 		success = false;
287 		return 0;
288 	}
289 
290 	//------------------------------
getNewParamIndex(const COLLADAFW::Formula * formula,const String & parameterName,bool & found)291 	size_t FormulasLinker::getNewParamIndex(const COLLADAFW::Formula* formula, const String& parameterName, bool &found)
292 	{
293 		const COLLADAFW::FormulaNewParamPointerArray& newParams = formula->getNewParams();
294 		for ( size_t i = 0, count = newParams.getCount(); i < count; ++i)
295 		{
296 			const COLLADAFW::FormulaNewParam* newParam = newParams[i];
297 			if ( newParam->getName() == parameterName )
298 			{
299 				found = true;
300 				return i;
301 			}
302 		}
303 		found = false;
304 		return 0;
305 	}
306 
307 } // namespace COLLADASaxFWL
308