/*
 * Copyright (C) 2006 The Android Open Source Project
 *
 * Licensed 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.
 */

#ifndef SkXMLParser_DEFINED
#define SkXMLParser_DEFINED

#include "SkMath.h"
#include "SkString.h"

class SkStream;

class SkDOM;
struct SkDOMNode;

class SkXMLParserError {
public:
    enum ErrorCode {
        kNoError,
        kEmptyFile,
        kUnknownElement,
        kUnknownAttributeName,
        kErrorInAttributeValue,
        kDuplicateIDs,
        kUnknownError
    };

    SkXMLParserError();
    virtual ~SkXMLParserError();
    ErrorCode getErrorCode() const { return fCode; }
    virtual void getErrorString(SkString* str) const;
    int getLineNumber() const { return fLineNumber; }
    int getNativeCode() const { return fNativeCode; }
    bool hasError() const { return fCode != kNoError || fNativeCode != -1; }
    bool hasNoun() const { return fNoun.size() > 0; }
    void reset();
    void setCode(ErrorCode code) { fCode = code; }
    void setNoun(const SkString& str) { fNoun.set(str); }
    void setNoun(const char* ch)  { fNoun.set(ch); }
    void setNoun(const char* ch, size_t len) { fNoun.set(ch, len); }
protected:
    ErrorCode fCode;
private:
    int fLineNumber;
    int fNativeCode;
    SkString fNoun;
    friend class SkXMLParser;
};

class SkXMLParser {
public:
            SkXMLParser(SkXMLParserError* parserError = NULL);
    virtual ~SkXMLParser();

    /** Returns true for success
    */
    bool parse(const char doc[], size_t len);
    bool parse(SkStream& docStream);
    bool parse(const SkDOM&, const SkDOMNode*);

    static void GetNativeErrorString(int nativeErrorCode, SkString* str);

protected:
    // override in subclasses; return true to stop parsing
    virtual bool onStartElement(const char elem[]);
    virtual bool onAddAttribute(const char name[], const char value[]);
    virtual bool onEndElement(const char elem[]);
    virtual bool onText(const char text[], int len);

public:
    // public for ported implementation, not meant for clients to call
    virtual bool startElement(const char elem[]);
    virtual bool addAttribute(const char name[], const char value[]);
    virtual bool endElement(const char elem[]);
    virtual bool text(const char text[], int len); 
    void* fParser;
protected:
    SkXMLParserError* fError;
private:
    void reportError(void* parser);
};

class SkXMLPullParser {
public:
            SkXMLPullParser();
    explicit SkXMLPullParser(SkStream*);
    virtual ~SkXMLPullParser();

    SkStream*   getStream() const { return fStream; }
    SkStream*   setStream(SkStream* stream);

    enum EventType {
        ERROR = -1,
        START_DOCUMENT,
        END_DOCUMENT,
        START_TAG,
        END_TAG,
        TEXT,
        CDSECT,
        ENTITY_REF,
        IGNORABLE_WHITESPACE,
        PROCESSING_INSTRUCTION,
        COMMENT,
        DOCDECL
    };

    EventType   nextToken();
    EventType   getEventType() const { return fCurr.fEventType; }

    struct AttrInfo {
        const char* fName;
        const char* fValue;
    };
    
    int         getDepth() const { return fDepth; }
    const char* getName();
    int         getAttributeCount();
    void        getAttributeInfo(int, AttrInfo*);
    const char* getText();
    bool        isWhitespace();
    
protected:
    virtual bool onEntityReplacement(const char name[],
                                     SkString* replacement);

public:
    struct Curr {
        EventType   fEventType;
        const char* fName;
        AttrInfo*   fAttrInfos;
        int         fAttrInfoCount;
        bool        fIsWhitespace;
    };

private:
    // implemented in the porting layer
    bool        onInit();   // return false on failure
    EventType   onNextToken();
    void        onExit();
    
    SkStream*   fStream;
    Curr        fCurr;
    int         fDepth;
    
    struct Impl;
    Impl*   fImpl;
};

#endif