blob: 3ebf9dc4fe5c13c81dff70c2cba2ea63b7e1d7ac [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
2/*
3 * Copyright 2006 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
reed@android.com8a1c16f2008-12-17 15:59:43 +00009
10#include "SkDisplayXMLParser.h"
11#include "SkAnimateMaker.h"
12#include "SkDisplayApply.h"
13#include "SkUtils.h"
14#ifdef SK_DEBUG
15#include "SkTime.h"
16#endif
17
18static char const* const gErrorStrings[] = {
19 "unknown error ",
20 "apply scopes itself",
21 "display tree too deep (circular reference?) ",
22 "element missing parent ",
23 "element type not allowed in parent ",
24 "error adding <data> to <post> ",
25 "error adding to <matrix> ",
26 "error adding to <paint> ",
27 "error adding to <path> ",
28 "error in attribute value ",
29 "error in script ",
30 "expected movie in sink attribute ",
31 "field not in target ",
32 "number of offsets in gradient must match number of colors",
33 "no offset in gradient may be greater than one",
34 "last offset in gradient must be one",
35 "offsets in gradient must be increasing",
36 "first offset in gradient must be zero",
rmistry@google.comd6176b02012-08-23 18:14:13 +000037 "gradient attribute \"points\" must have length of four",
reed@android.com8a1c16f2008-12-17 15:59:43 +000038 "in include ",
39 "in movie ",
40 "include name unknown or missing ",
41 "index out of range ",
42 "movie name unknown or missing ",
43 "no parent available to resolve sink attribute ",
44 "parent element can't contain ",
45 "saveLayer must specify a bounds",
46 "target id not found ",
47 "unexpected type "
48};
49
50SkDisplayXMLParserError::~SkDisplayXMLParserError() {
51}
52
53void SkDisplayXMLParserError::getErrorString(SkString* str) const {
54 if (fCode > kUnknownError)
55 str->set(gErrorStrings[fCode - kUnknownError]);
56 else
57 str->reset();
58 INHERITED::getErrorString(str);
59}
60
61void SkDisplayXMLParserError::setInnerError(SkAnimateMaker* parent, const SkString& src) {
62 SkString inner;
63 getErrorString(&inner);
64 inner.prepend(": ");
65 inner.prependS32(getLineNumber());
66 inner.prepend(", line ");
67 inner.prepend(src);
68 parent->setErrorNoun(inner);
69}
70
71
72SkDisplayXMLParser::SkDisplayXMLParser(SkAnimateMaker& maker)
rmistry@google.comd6176b02012-08-23 18:14:13 +000073 : INHERITED(&maker.fError), fMaker(maker), fInInclude(maker.fInInclude),
reed@android.com8a1c16f2008-12-17 15:59:43 +000074 fInSkia(maker.fInInclude), fCurrDisplayable(NULL)
75{
76}
77
78SkDisplayXMLParser::~SkDisplayXMLParser() {
79 if (fCurrDisplayable && fMaker.fChildren.find(fCurrDisplayable) < 0)
80 delete fCurrDisplayable;
81 for (Parent* parPtr = fParents.begin() + 1; parPtr < fParents.end(); parPtr++) {
82 SkDisplayable* displayable = parPtr->fDisplayable;
83 if (displayable == fCurrDisplayable)
84 continue;
85 SkASSERT(fMaker.fChildren.find(displayable) < 0);
86 if (fMaker.fHelpers.find(displayable) < 0)
87 delete displayable;
88 }
89}
90
91
92
93bool SkDisplayXMLParser::onAddAttribute(const char name[], const char value[]) {
94 return onAddAttributeLen(name, value, strlen(value));
95}
96
rmistry@google.comd6176b02012-08-23 18:14:13 +000097bool SkDisplayXMLParser::onAddAttributeLen(const char attrName[], const char attrValue[],
reed@android.com8a1c16f2008-12-17 15:59:43 +000098 size_t attrValueLen)
99{
100 if (fCurrDisplayable == NULL) // this signals we should ignore attributes for this element
101 return strncmp(attrName, "xmlns", sizeof("xmlns") - 1) != 0;
102 SkDisplayable* displayable = fCurrDisplayable;
103 SkDisplayTypes type = fCurrType;
104
105 if (strcmp(attrName, "id") == 0) {
106 if (fMaker.find(attrValue, attrValueLen, NULL)) {
107 fError->setNoun(attrValue, attrValueLen);
108 fError->setCode(SkXMLParserError::kDuplicateIDs);
109 return true;
110 }
111#ifdef SK_DEBUG
112 displayable->_id.set(attrValue, attrValueLen);
113 displayable->id = displayable->_id.c_str();
114#endif
115 fMaker.idsSet(attrValue, attrValueLen, displayable);
116 int parentIndex = fParents.count() - 1;
117 if (parentIndex > 0) {
118 SkDisplayable* parent = fParents[parentIndex - 1].fDisplayable;
119 parent->setChildHasID();
120 }
121 return false;
122 }
123 const char* name = attrName;
124 const SkMemberInfo* info = SkDisplayType::GetMember(&fMaker, type, &name);
125 if (info == NULL) {
126 fError->setNoun(name);
127 fError->setCode(SkXMLParserError::kUnknownAttributeName);
128 return true;
129 }
130 if (info->setValue(fMaker, NULL, 0, info->getCount(), displayable, info->getType(), attrValue,
131 attrValueLen))
132 return false;
133 if (fMaker.fError.hasError()) {
134 fError->setNoun(attrValue, attrValueLen);
135 return true;
136 }
137 SkDisplayable* ref = NULL;
138 if (fMaker.find(attrValue, attrValueLen, &ref) == false) {
139 ref = fMaker.createInstance(attrValue, attrValueLen);
140 if (ref == NULL) {
141 fError->setNoun(attrValue, attrValueLen);
142 fError->setCode(SkXMLParserError::kErrorInAttributeValue);
143 return true;
144 } else
145 fMaker.helperAdd(ref);
146 }
147 if (info->fType != SkType_MemberProperty) {
148 fError->setNoun(name);
149 fError->setCode(SkXMLParserError::kUnknownAttributeName);
150 return true;
151 }
152 SkScriptValue scriptValue;
153 scriptValue.fOperand.fDisplayable = ref;
154 scriptValue.fType = ref->getType();
155 displayable->setProperty(info->propertyIndex(), scriptValue);
156 return false;
157}
158
bsalomon@google.comc04e6d52011-03-22 20:53:31 +0000159#if defined(SK_BUILD_FOR_WIN32)
bsalomon@google.com9d12f5c2011-09-29 18:08:18 +0000160 #define SK_strcasecmp _stricmp
161 #define SK_strncasecmp _strnicmp
bsalomon@google.comc8ad63e2011-03-18 14:29:44 +0000162#else
163 #define SK_strcasecmp strcasecmp
164 #define SK_strncasecmp strncasecmp
165#endif
166
reed@android.com8a1c16f2008-12-17 15:59:43 +0000167bool SkDisplayXMLParser::onEndElement(const char elem[])
168{
169 int parentIndex = fParents.count() - 1;
170 if (parentIndex >= 0) {
171 Parent& container = fParents[parentIndex];
172 SkDisplayable* displayable = container.fDisplayable;
173 fMaker.fEndDepth = parentIndex;
174 displayable->onEndElement(fMaker);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000175 if (fMaker.fError.hasError())
reed@android.com8a1c16f2008-12-17 15:59:43 +0000176 return true;
177 if (parentIndex > 0) {
178 SkDisplayable* parent = fParents[parentIndex - 1].fDisplayable;
tfarina@chromium.org1d3c4112012-12-03 14:38:08 +0000179 bool result = parent->addChild(fMaker, displayable);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000180 if (fMaker.hasError())
reed@android.com8a1c16f2008-12-17 15:59:43 +0000181 return true;
182 if (result == false) {
183 int infoCount;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000184 const SkMemberInfo* info =
reed@android.com8a1c16f2008-12-17 15:59:43 +0000185 SkDisplayType::GetMembers(&fMaker, fParents[parentIndex - 1].fType, &infoCount);
186 const SkMemberInfo* foundInfo;
187 if ((foundInfo = searchContainer(info, infoCount)) != NULL) {
188 parent->setReference(foundInfo, displayable);
189 // if (displayable->isHelper() == false)
190 fMaker.helperAdd(displayable);
191 } else {
192 fMaker.setErrorCode(SkDisplayXMLParserError::kElementTypeNotAllowedInParent);
193 return true;
194 }
195 }
196 if (parent->childrenNeedDisposing())
197 delete displayable;
198 }
199 fParents.remove(parentIndex);
200 }
201 fCurrDisplayable = NULL;
bsalomon@google.comc8ad63e2011-03-18 14:29:44 +0000202 if (fInInclude == false && SK_strcasecmp(elem, "screenplay") == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000203 if (fMaker.fInMovie == false) {
204 fMaker.fEnableTime = fMaker.getAppTime();
205#if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING
206 if (fMaker.fDebugTimeBase == (SkMSec) -1)
207 fMaker.fDebugTimeBase = fMaker.fEnableTime;
208 SkString debugOut;
209 SkMSec time = fMaker.getAppTime();
210 debugOut.appendS32(time - fMaker.fDebugTimeBase);
211 debugOut.append(" onLoad enable=");
212 debugOut.appendS32(fMaker.fEnableTime - fMaker.fDebugTimeBase);
213 SkDebugf("%s\n", debugOut.c_str());
214#endif
215 fMaker.fEvents.doEvent(fMaker, SkDisplayEvent::kOnload, NULL);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000216 if (fMaker.fError.hasError())
reed@android.com8a1c16f2008-12-17 15:59:43 +0000217 return true;
218 fMaker.fEvents.removeEvent(SkDisplayEvent::kOnload, NULL);
219
220 }
221 fInSkia = false;
222 }
223 return false;
224}
225
226bool SkDisplayXMLParser::onStartElement(const char name[])
227{
228 return onStartElementLen(name, strlen(name));
229}
230
231bool SkDisplayXMLParser::onStartElementLen(const char name[], size_t len) {
232 fCurrDisplayable = NULL; // init so we'll ignore attributes if we exit early
233
bsalomon@google.comc8ad63e2011-03-18 14:29:44 +0000234 if (SK_strncasecmp(name, "screenplay", len) == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000235 fInSkia = true;
236 if (fInInclude == false)
237 fMaker.idsSet(name, len, &fMaker.fScreenplay);
238 return false;
239 }
240 if (fInSkia == false)
241 return false;
242
243 SkDisplayable* displayable = fMaker.createInstance(name, len);
244 if (displayable == NULL) {
245 fError->setNoun(name, len);
246 fError->setCode(SkXMLParserError::kUnknownElement);
247 return true;
248 }
249 SkDisplayTypes type = displayable->getType();
250 Parent record = { displayable, type };
251 *fParents.append() = record;
252 if (fParents.count() == 1)
253 fMaker.childrenAdd(displayable);
254 else {
255 Parent* parent = fParents.end() - 2;
256 if (displayable->setParent(parent->fDisplayable)) {
257 fError->setNoun(name, len);
258 getError()->setCode(SkDisplayXMLParserError::kParentElementCantContain);
259 return true;
260 }
261 }
262
263 // set these for subsequent calls to addAttribute()
264 fCurrDisplayable = displayable;
265 fCurrType = type;
266 return false;
267}
268
269const SkMemberInfo* SkDisplayXMLParser::searchContainer(const SkMemberInfo* infoBase,
270 int infoCount) {
271 const SkMemberInfo* bestDisplayable = NULL;
272 const SkMemberInfo* lastResort = NULL;
273 for (int index = 0; index < infoCount; index++) {
274 const SkMemberInfo* info = &infoBase[index];
275 if (info->fType == SkType_BaseClassInfo) {
276 const SkMemberInfo* inherited = info->getInherited();
277 const SkMemberInfo* result = searchContainer(inherited, info->fCount);
278 if (result != NULL)
279 return result;
280 continue;
281 }
282 Parent* container = fParents.end() - 1;
283 SkDisplayTypes type = (SkDisplayTypes) info->fType;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000284 if (type == SkType_MemberProperty)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000285 type = info->propertyType();
286 SkDisplayTypes containerType = container->fType;
287 if (type == containerType && (type == SkType_Rect || type == SkType_Polygon ||
288 type == SkType_Array || type == SkType_Int || type == SkType_Bitmap))
289 goto rectNext;
290 while (type != containerType) {
291 if (containerType == SkType_Displayable)
292 goto next;
293 containerType = SkDisplayType::GetParent(&fMaker, containerType);
294 if (containerType == SkType_Unknown)
295 goto next;
296 }
297 return info;
298next:
rmistry@google.comd6176b02012-08-23 18:14:13 +0000299 if (type == SkType_Drawable || (type == SkType_Displayable &&
senorblanco@chromium.org64cc5792011-05-19 19:58:58 +0000300 container->fDisplayable->isDrawable())) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000301rectNext:
302 if (fParents.count() > 1) {
303 Parent* parent = fParents.end() - 2;
304 if (info == parent->fDisplayable->preferredChild(type))
305 bestDisplayable = info;
306 else
307 lastResort = info;
308 }
309 }
310 }
311 if (bestDisplayable)
312 return bestDisplayable;
313 if (lastResort)
314 return lastResort;
315 return NULL;
316}