blob: db6c83848cb0683517511a99e9ca2f64baee2f09 [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/* libs/graphics/animator/SkDisplayXMLParser.cpp
2**
3** Copyright 2006, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9** http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18#include "SkDisplayXMLParser.h"
19#include "SkAnimateMaker.h"
20#include "SkDisplayApply.h"
21#include "SkUtils.h"
22#ifdef SK_DEBUG
23#include "SkTime.h"
24#endif
25
26static char const* const gErrorStrings[] = {
27 "unknown error ",
28 "apply scopes itself",
29 "display tree too deep (circular reference?) ",
30 "element missing parent ",
31 "element type not allowed in parent ",
32 "error adding <data> to <post> ",
33 "error adding to <matrix> ",
34 "error adding to <paint> ",
35 "error adding to <path> ",
36 "error in attribute value ",
37 "error in script ",
38 "expected movie in sink attribute ",
39 "field not in target ",
40 "number of offsets in gradient must match number of colors",
41 "no offset in gradient may be greater than one",
42 "last offset in gradient must be one",
43 "offsets in gradient must be increasing",
44 "first offset in gradient must be zero",
45 "gradient attribute \"points\" must have length of four",
46 "in include ",
47 "in movie ",
48 "include name unknown or missing ",
49 "index out of range ",
50 "movie name unknown or missing ",
51 "no parent available to resolve sink attribute ",
52 "parent element can't contain ",
53 "saveLayer must specify a bounds",
54 "target id not found ",
55 "unexpected type "
56};
57
58SkDisplayXMLParserError::~SkDisplayXMLParserError() {
59}
60
61void SkDisplayXMLParserError::getErrorString(SkString* str) const {
62 if (fCode > kUnknownError)
63 str->set(gErrorStrings[fCode - kUnknownError]);
64 else
65 str->reset();
66 INHERITED::getErrorString(str);
67}
68
69void SkDisplayXMLParserError::setInnerError(SkAnimateMaker* parent, const SkString& src) {
70 SkString inner;
71 getErrorString(&inner);
72 inner.prepend(": ");
73 inner.prependS32(getLineNumber());
74 inner.prepend(", line ");
75 inner.prepend(src);
76 parent->setErrorNoun(inner);
77}
78
79
80SkDisplayXMLParser::SkDisplayXMLParser(SkAnimateMaker& maker)
81 : INHERITED(&maker.fError), fMaker(maker), fInInclude(maker.fInInclude),
82 fInSkia(maker.fInInclude), fCurrDisplayable(NULL)
83{
84}
85
86SkDisplayXMLParser::~SkDisplayXMLParser() {
87 if (fCurrDisplayable && fMaker.fChildren.find(fCurrDisplayable) < 0)
88 delete fCurrDisplayable;
89 for (Parent* parPtr = fParents.begin() + 1; parPtr < fParents.end(); parPtr++) {
90 SkDisplayable* displayable = parPtr->fDisplayable;
91 if (displayable == fCurrDisplayable)
92 continue;
93 SkASSERT(fMaker.fChildren.find(displayable) < 0);
94 if (fMaker.fHelpers.find(displayable) < 0)
95 delete displayable;
96 }
97}
98
99
100
101bool SkDisplayXMLParser::onAddAttribute(const char name[], const char value[]) {
102 return onAddAttributeLen(name, value, strlen(value));
103}
104
105bool SkDisplayXMLParser::onAddAttributeLen(const char attrName[], const char attrValue[],
106 size_t attrValueLen)
107{
108 if (fCurrDisplayable == NULL) // this signals we should ignore attributes for this element
109 return strncmp(attrName, "xmlns", sizeof("xmlns") - 1) != 0;
110 SkDisplayable* displayable = fCurrDisplayable;
111 SkDisplayTypes type = fCurrType;
112
113 if (strcmp(attrName, "id") == 0) {
114 if (fMaker.find(attrValue, attrValueLen, NULL)) {
115 fError->setNoun(attrValue, attrValueLen);
116 fError->setCode(SkXMLParserError::kDuplicateIDs);
117 return true;
118 }
119#ifdef SK_DEBUG
120 displayable->_id.set(attrValue, attrValueLen);
121 displayable->id = displayable->_id.c_str();
122#endif
123 fMaker.idsSet(attrValue, attrValueLen, displayable);
124 int parentIndex = fParents.count() - 1;
125 if (parentIndex > 0) {
126 SkDisplayable* parent = fParents[parentIndex - 1].fDisplayable;
127 parent->setChildHasID();
128 }
129 return false;
130 }
131 const char* name = attrName;
132 const SkMemberInfo* info = SkDisplayType::GetMember(&fMaker, type, &name);
133 if (info == NULL) {
134 fError->setNoun(name);
135 fError->setCode(SkXMLParserError::kUnknownAttributeName);
136 return true;
137 }
138 if (info->setValue(fMaker, NULL, 0, info->getCount(), displayable, info->getType(), attrValue,
139 attrValueLen))
140 return false;
141 if (fMaker.fError.hasError()) {
142 fError->setNoun(attrValue, attrValueLen);
143 return true;
144 }
145 SkDisplayable* ref = NULL;
146 if (fMaker.find(attrValue, attrValueLen, &ref) == false) {
147 ref = fMaker.createInstance(attrValue, attrValueLen);
148 if (ref == NULL) {
149 fError->setNoun(attrValue, attrValueLen);
150 fError->setCode(SkXMLParserError::kErrorInAttributeValue);
151 return true;
152 } else
153 fMaker.helperAdd(ref);
154 }
155 if (info->fType != SkType_MemberProperty) {
156 fError->setNoun(name);
157 fError->setCode(SkXMLParserError::kUnknownAttributeName);
158 return true;
159 }
160 SkScriptValue scriptValue;
161 scriptValue.fOperand.fDisplayable = ref;
162 scriptValue.fType = ref->getType();
163 displayable->setProperty(info->propertyIndex(), scriptValue);
164 return false;
165}
166
bsalomon@google.comc04e6d52011-03-22 20:53:31 +0000167#if defined(SK_BUILD_FOR_WIN32)
bsalomon@google.comc8ad63e2011-03-18 14:29:44 +0000168 #define SK_strcasecmp stricmp
169 #define SK_strncasecmp strnicmp
170#else
171 #define SK_strcasecmp strcasecmp
172 #define SK_strncasecmp strncasecmp
173#endif
174
reed@android.com8a1c16f2008-12-17 15:59:43 +0000175bool SkDisplayXMLParser::onEndElement(const char elem[])
176{
177 int parentIndex = fParents.count() - 1;
178 if (parentIndex >= 0) {
179 Parent& container = fParents[parentIndex];
180 SkDisplayable* displayable = container.fDisplayable;
181 fMaker.fEndDepth = parentIndex;
182 displayable->onEndElement(fMaker);
183 if (fMaker.fError.hasError())
184 return true;
185 if (parentIndex > 0) {
186 SkDisplayable* parent = fParents[parentIndex - 1].fDisplayable;
187 bool result = parent->add(fMaker, displayable);
188 if (fMaker.hasError())
189 return true;
190 if (result == false) {
191 int infoCount;
192 const SkMemberInfo* info =
193 SkDisplayType::GetMembers(&fMaker, fParents[parentIndex - 1].fType, &infoCount);
194 const SkMemberInfo* foundInfo;
195 if ((foundInfo = searchContainer(info, infoCount)) != NULL) {
196 parent->setReference(foundInfo, displayable);
197 // if (displayable->isHelper() == false)
198 fMaker.helperAdd(displayable);
199 } else {
200 fMaker.setErrorCode(SkDisplayXMLParserError::kElementTypeNotAllowedInParent);
201 return true;
202 }
203 }
204 if (parent->childrenNeedDisposing())
205 delete displayable;
206 }
207 fParents.remove(parentIndex);
208 }
209 fCurrDisplayable = NULL;
bsalomon@google.comc8ad63e2011-03-18 14:29:44 +0000210 if (fInInclude == false && SK_strcasecmp(elem, "screenplay") == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000211 if (fMaker.fInMovie == false) {
212 fMaker.fEnableTime = fMaker.getAppTime();
213#if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING
214 if (fMaker.fDebugTimeBase == (SkMSec) -1)
215 fMaker.fDebugTimeBase = fMaker.fEnableTime;
216 SkString debugOut;
217 SkMSec time = fMaker.getAppTime();
218 debugOut.appendS32(time - fMaker.fDebugTimeBase);
219 debugOut.append(" onLoad enable=");
220 debugOut.appendS32(fMaker.fEnableTime - fMaker.fDebugTimeBase);
221 SkDebugf("%s\n", debugOut.c_str());
222#endif
223 fMaker.fEvents.doEvent(fMaker, SkDisplayEvent::kOnload, NULL);
224 if (fMaker.fError.hasError())
225 return true;
226 fMaker.fEvents.removeEvent(SkDisplayEvent::kOnload, NULL);
227
228 }
229 fInSkia = false;
230 }
231 return false;
232}
233
234bool SkDisplayXMLParser::onStartElement(const char name[])
235{
236 return onStartElementLen(name, strlen(name));
237}
238
239bool SkDisplayXMLParser::onStartElementLen(const char name[], size_t len) {
240 fCurrDisplayable = NULL; // init so we'll ignore attributes if we exit early
241
bsalomon@google.comc8ad63e2011-03-18 14:29:44 +0000242 if (SK_strncasecmp(name, "screenplay", len) == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000243 fInSkia = true;
244 if (fInInclude == false)
245 fMaker.idsSet(name, len, &fMaker.fScreenplay);
246 return false;
247 }
248 if (fInSkia == false)
249 return false;
250
251 SkDisplayable* displayable = fMaker.createInstance(name, len);
252 if (displayable == NULL) {
253 fError->setNoun(name, len);
254 fError->setCode(SkXMLParserError::kUnknownElement);
255 return true;
256 }
257 SkDisplayTypes type = displayable->getType();
258 Parent record = { displayable, type };
259 *fParents.append() = record;
260 if (fParents.count() == 1)
261 fMaker.childrenAdd(displayable);
262 else {
263 Parent* parent = fParents.end() - 2;
264 if (displayable->setParent(parent->fDisplayable)) {
265 fError->setNoun(name, len);
266 getError()->setCode(SkDisplayXMLParserError::kParentElementCantContain);
267 return true;
268 }
269 }
270
271 // set these for subsequent calls to addAttribute()
272 fCurrDisplayable = displayable;
273 fCurrType = type;
274 return false;
275}
276
277const SkMemberInfo* SkDisplayXMLParser::searchContainer(const SkMemberInfo* infoBase,
278 int infoCount) {
279 const SkMemberInfo* bestDisplayable = NULL;
280 const SkMemberInfo* lastResort = NULL;
281 for (int index = 0; index < infoCount; index++) {
282 const SkMemberInfo* info = &infoBase[index];
283 if (info->fType == SkType_BaseClassInfo) {
284 const SkMemberInfo* inherited = info->getInherited();
285 const SkMemberInfo* result = searchContainer(inherited, info->fCount);
286 if (result != NULL)
287 return result;
288 continue;
289 }
290 Parent* container = fParents.end() - 1;
291 SkDisplayTypes type = (SkDisplayTypes) info->fType;
292 if (type == SkType_MemberProperty)
293 type = info->propertyType();
294 SkDisplayTypes containerType = container->fType;
295 if (type == containerType && (type == SkType_Rect || type == SkType_Polygon ||
296 type == SkType_Array || type == SkType_Int || type == SkType_Bitmap))
297 goto rectNext;
298 while (type != containerType) {
299 if (containerType == SkType_Displayable)
300 goto next;
301 containerType = SkDisplayType::GetParent(&fMaker, containerType);
302 if (containerType == SkType_Unknown)
303 goto next;
304 }
305 return info;
306next:
senorblanco@chromium.org64cc5792011-05-19 19:58:58 +0000307 if (type == SkType_Drawable || (type == SkType_Displayable &&
308 container->fDisplayable->isDrawable())) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000309rectNext:
310 if (fParents.count() > 1) {
311 Parent* parent = fParents.end() - 2;
312 if (info == parent->fDisplayable->preferredChild(type))
313 bestDisplayable = info;
314 else
315 lastResort = info;
316 }
317 }
318 }
319 if (bestDisplayable)
320 return bestDisplayable;
321 if (lastResort)
322 return lastResort;
323 return NULL;
324}
325
326