michael@0: michael@0: /* michael@0: * Copyright 2006 The Android Open Source Project michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: michael@0: #include "SkDisplayXMLParser.h" michael@0: #include "SkAnimateMaker.h" michael@0: #include "SkDisplayApply.h" michael@0: #include "SkUtils.h" michael@0: #ifdef SK_DEBUG michael@0: #include "SkTime.h" michael@0: #endif michael@0: michael@0: static char const* const gErrorStrings[] = { michael@0: "unknown error ", michael@0: "apply scopes itself", michael@0: "display tree too deep (circular reference?) ", michael@0: "element missing parent ", michael@0: "element type not allowed in parent ", michael@0: "error adding to ", michael@0: "error adding to ", michael@0: "error adding to ", michael@0: "error adding to ", michael@0: "error in attribute value ", michael@0: "error in script ", michael@0: "expected movie in sink attribute ", michael@0: "field not in target ", michael@0: "number of offsets in gradient must match number of colors", michael@0: "no offset in gradient may be greater than one", michael@0: "last offset in gradient must be one", michael@0: "offsets in gradient must be increasing", michael@0: "first offset in gradient must be zero", michael@0: "gradient attribute \"points\" must have length of four", michael@0: "in include ", michael@0: "in movie ", michael@0: "include name unknown or missing ", michael@0: "index out of range ", michael@0: "movie name unknown or missing ", michael@0: "no parent available to resolve sink attribute ", michael@0: "parent element can't contain ", michael@0: "saveLayer must specify a bounds", michael@0: "target id not found ", michael@0: "unexpected type " michael@0: }; michael@0: michael@0: SkDisplayXMLParserError::~SkDisplayXMLParserError() { michael@0: } michael@0: michael@0: void SkDisplayXMLParserError::getErrorString(SkString* str) const { michael@0: if (fCode > kUnknownError) michael@0: str->set(gErrorStrings[fCode - kUnknownError]); michael@0: else michael@0: str->reset(); michael@0: INHERITED::getErrorString(str); michael@0: } michael@0: michael@0: void SkDisplayXMLParserError::setInnerError(SkAnimateMaker* parent, const SkString& src) { michael@0: SkString inner; michael@0: getErrorString(&inner); michael@0: inner.prepend(": "); michael@0: inner.prependS32(getLineNumber()); michael@0: inner.prepend(", line "); michael@0: inner.prepend(src); michael@0: parent->setErrorNoun(inner); michael@0: } michael@0: michael@0: michael@0: SkDisplayXMLParser::SkDisplayXMLParser(SkAnimateMaker& maker) michael@0: : INHERITED(&maker.fError), fMaker(maker), fInInclude(maker.fInInclude), michael@0: fInSkia(maker.fInInclude), fCurrDisplayable(NULL) michael@0: { michael@0: } michael@0: michael@0: SkDisplayXMLParser::~SkDisplayXMLParser() { michael@0: if (fCurrDisplayable && fMaker.fChildren.find(fCurrDisplayable) < 0) michael@0: delete fCurrDisplayable; michael@0: for (Parent* parPtr = fParents.begin() + 1; parPtr < fParents.end(); parPtr++) { michael@0: SkDisplayable* displayable = parPtr->fDisplayable; michael@0: if (displayable == fCurrDisplayable) michael@0: continue; michael@0: SkASSERT(fMaker.fChildren.find(displayable) < 0); michael@0: if (fMaker.fHelpers.find(displayable) < 0) michael@0: delete displayable; michael@0: } michael@0: } michael@0: michael@0: michael@0: michael@0: bool SkDisplayXMLParser::onAddAttribute(const char name[], const char value[]) { michael@0: return onAddAttributeLen(name, value, strlen(value)); michael@0: } michael@0: michael@0: bool SkDisplayXMLParser::onAddAttributeLen(const char attrName[], const char attrValue[], michael@0: size_t attrValueLen) michael@0: { michael@0: if (fCurrDisplayable == NULL) // this signals we should ignore attributes for this element michael@0: return strncmp(attrName, "xmlns", sizeof("xmlns") - 1) != 0; michael@0: SkDisplayable* displayable = fCurrDisplayable; michael@0: SkDisplayTypes type = fCurrType; michael@0: michael@0: if (strcmp(attrName, "id") == 0) { michael@0: if (fMaker.find(attrValue, attrValueLen, NULL)) { michael@0: fError->setNoun(attrValue, attrValueLen); michael@0: fError->setCode(SkXMLParserError::kDuplicateIDs); michael@0: return true; michael@0: } michael@0: #ifdef SK_DEBUG michael@0: displayable->_id.set(attrValue, attrValueLen); michael@0: displayable->id = displayable->_id.c_str(); michael@0: #endif michael@0: fMaker.idsSet(attrValue, attrValueLen, displayable); michael@0: int parentIndex = fParents.count() - 1; michael@0: if (parentIndex > 0) { michael@0: SkDisplayable* parent = fParents[parentIndex - 1].fDisplayable; michael@0: parent->setChildHasID(); michael@0: } michael@0: return false; michael@0: } michael@0: const char* name = attrName; michael@0: const SkMemberInfo* info = SkDisplayType::GetMember(&fMaker, type, &name); michael@0: if (info == NULL) { michael@0: fError->setNoun(name); michael@0: fError->setCode(SkXMLParserError::kUnknownAttributeName); michael@0: return true; michael@0: } michael@0: if (info->setValue(fMaker, NULL, 0, info->getCount(), displayable, info->getType(), attrValue, michael@0: attrValueLen)) michael@0: return false; michael@0: if (fMaker.fError.hasError()) { michael@0: fError->setNoun(attrValue, attrValueLen); michael@0: return true; michael@0: } michael@0: SkDisplayable* ref = NULL; michael@0: if (fMaker.find(attrValue, attrValueLen, &ref) == false) { michael@0: ref = fMaker.createInstance(attrValue, attrValueLen); michael@0: if (ref == NULL) { michael@0: fError->setNoun(attrValue, attrValueLen); michael@0: fError->setCode(SkXMLParserError::kErrorInAttributeValue); michael@0: return true; michael@0: } else michael@0: fMaker.helperAdd(ref); michael@0: } michael@0: if (info->fType != SkType_MemberProperty) { michael@0: fError->setNoun(name); michael@0: fError->setCode(SkXMLParserError::kUnknownAttributeName); michael@0: return true; michael@0: } michael@0: SkScriptValue scriptValue; michael@0: scriptValue.fOperand.fDisplayable = ref; michael@0: scriptValue.fType = ref->getType(); michael@0: displayable->setProperty(info->propertyIndex(), scriptValue); michael@0: return false; michael@0: } michael@0: michael@0: #if defined(SK_BUILD_FOR_WIN32) michael@0: #define SK_strcasecmp _stricmp michael@0: #define SK_strncasecmp _strnicmp michael@0: #else michael@0: #define SK_strcasecmp strcasecmp michael@0: #define SK_strncasecmp strncasecmp michael@0: #endif michael@0: michael@0: bool SkDisplayXMLParser::onEndElement(const char elem[]) michael@0: { michael@0: int parentIndex = fParents.count() - 1; michael@0: if (parentIndex >= 0) { michael@0: Parent& container = fParents[parentIndex]; michael@0: SkDisplayable* displayable = container.fDisplayable; michael@0: fMaker.fEndDepth = parentIndex; michael@0: displayable->onEndElement(fMaker); michael@0: if (fMaker.fError.hasError()) michael@0: return true; michael@0: if (parentIndex > 0) { michael@0: SkDisplayable* parent = fParents[parentIndex - 1].fDisplayable; michael@0: bool result = parent->addChild(fMaker, displayable); michael@0: if (fMaker.hasError()) michael@0: return true; michael@0: if (result == false) { michael@0: int infoCount; michael@0: const SkMemberInfo* info = michael@0: SkDisplayType::GetMembers(&fMaker, fParents[parentIndex - 1].fType, &infoCount); michael@0: const SkMemberInfo* foundInfo; michael@0: if ((foundInfo = searchContainer(info, infoCount)) != NULL) { michael@0: parent->setReference(foundInfo, displayable); michael@0: // if (displayable->isHelper() == false) michael@0: fMaker.helperAdd(displayable); michael@0: } else { michael@0: fMaker.setErrorCode(SkDisplayXMLParserError::kElementTypeNotAllowedInParent); michael@0: return true; michael@0: } michael@0: } michael@0: if (parent->childrenNeedDisposing()) michael@0: delete displayable; michael@0: } michael@0: fParents.remove(parentIndex); michael@0: } michael@0: fCurrDisplayable = NULL; michael@0: if (fInInclude == false && SK_strcasecmp(elem, "screenplay") == 0) { michael@0: if (fMaker.fInMovie == false) { michael@0: fMaker.fEnableTime = fMaker.getAppTime(); michael@0: #if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING michael@0: if (fMaker.fDebugTimeBase == (SkMSec) -1) michael@0: fMaker.fDebugTimeBase = fMaker.fEnableTime; michael@0: SkString debugOut; michael@0: SkMSec time = fMaker.getAppTime(); michael@0: debugOut.appendS32(time - fMaker.fDebugTimeBase); michael@0: debugOut.append(" onLoad enable="); michael@0: debugOut.appendS32(fMaker.fEnableTime - fMaker.fDebugTimeBase); michael@0: SkDebugf("%s\n", debugOut.c_str()); michael@0: #endif michael@0: fMaker.fEvents.doEvent(fMaker, SkDisplayEvent::kOnload, NULL); michael@0: if (fMaker.fError.hasError()) michael@0: return true; michael@0: fMaker.fEvents.removeEvent(SkDisplayEvent::kOnload, NULL); michael@0: michael@0: } michael@0: fInSkia = false; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: bool SkDisplayXMLParser::onStartElement(const char name[]) michael@0: { michael@0: return onStartElementLen(name, strlen(name)); michael@0: } michael@0: michael@0: bool SkDisplayXMLParser::onStartElementLen(const char name[], size_t len) { michael@0: fCurrDisplayable = NULL; // init so we'll ignore attributes if we exit early michael@0: michael@0: if (SK_strncasecmp(name, "screenplay", len) == 0) { michael@0: fInSkia = true; michael@0: if (fInInclude == false) michael@0: fMaker.idsSet(name, len, &fMaker.fScreenplay); michael@0: return false; michael@0: } michael@0: if (fInSkia == false) michael@0: return false; michael@0: michael@0: SkDisplayable* displayable = fMaker.createInstance(name, len); michael@0: if (displayable == NULL) { michael@0: fError->setNoun(name, len); michael@0: fError->setCode(SkXMLParserError::kUnknownElement); michael@0: return true; michael@0: } michael@0: SkDisplayTypes type = displayable->getType(); michael@0: Parent record = { displayable, type }; michael@0: *fParents.append() = record; michael@0: if (fParents.count() == 1) michael@0: fMaker.childrenAdd(displayable); michael@0: else { michael@0: Parent* parent = fParents.end() - 2; michael@0: if (displayable->setParent(parent->fDisplayable)) { michael@0: fError->setNoun(name, len); michael@0: getError()->setCode(SkDisplayXMLParserError::kParentElementCantContain); michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: // set these for subsequent calls to addAttribute() michael@0: fCurrDisplayable = displayable; michael@0: fCurrType = type; michael@0: return false; michael@0: } michael@0: michael@0: const SkMemberInfo* SkDisplayXMLParser::searchContainer(const SkMemberInfo* infoBase, michael@0: int infoCount) { michael@0: const SkMemberInfo* bestDisplayable = NULL; michael@0: const SkMemberInfo* lastResort = NULL; michael@0: for (int index = 0; index < infoCount; index++) { michael@0: const SkMemberInfo* info = &infoBase[index]; michael@0: if (info->fType == SkType_BaseClassInfo) { michael@0: const SkMemberInfo* inherited = info->getInherited(); michael@0: const SkMemberInfo* result = searchContainer(inherited, info->fCount); michael@0: if (result != NULL) michael@0: return result; michael@0: continue; michael@0: } michael@0: Parent* container = fParents.end() - 1; michael@0: SkDisplayTypes type = (SkDisplayTypes) info->fType; michael@0: if (type == SkType_MemberProperty) michael@0: type = info->propertyType(); michael@0: SkDisplayTypes containerType = container->fType; michael@0: if (type == containerType && (type == SkType_Rect || type == SkType_Polygon || michael@0: type == SkType_Array || type == SkType_Int || type == SkType_Bitmap)) michael@0: goto rectNext; michael@0: while (type != containerType) { michael@0: if (containerType == SkType_Displayable) michael@0: goto next; michael@0: containerType = SkDisplayType::GetParent(&fMaker, containerType); michael@0: if (containerType == SkType_Unknown) michael@0: goto next; michael@0: } michael@0: return info; michael@0: next: michael@0: if (type == SkType_Drawable || (type == SkType_Displayable && michael@0: container->fDisplayable->isDrawable())) { michael@0: rectNext: michael@0: if (fParents.count() > 1) { michael@0: Parent* parent = fParents.end() - 2; michael@0: if (info == parent->fDisplayable->preferredChild(type)) michael@0: bestDisplayable = info; michael@0: else michael@0: lastResort = info; michael@0: } michael@0: } michael@0: } michael@0: if (bestDisplayable) michael@0: return bestDisplayable; michael@0: if (lastResort) michael@0: return lastResort; michael@0: return NULL; michael@0: }