public class

CLParser

extends java.lang.Object

 java.lang.Object

↳androidx.constraintlayout.core.parser.CLParser

Gradle dependencies

compile group: 'androidx.constraintlayout', name: 'constraintlayout-core', version: '1.1.0-beta01'

  • groupId: androidx.constraintlayout
  • artifactId: constraintlayout-core
  • version: 1.1.0-beta01

Artifact androidx.constraintlayout:constraintlayout-core:1.1.0-beta01 it located at Google repository (https://maven.google.com/)

Summary

Constructors
publicCLParser(java.lang.String content)

Methods
public CLObjectparse()

public static CLObjectparse(java.lang.String string)

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Constructors

public CLParser(java.lang.String content)

Methods

public static CLObject parse(java.lang.String string)

public CLObject parse()

Source

/*
 * Copyright (C) 2021 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.
 */
package androidx.constraintlayout.core.parser;

public class CLParser {

    static boolean sDebug = false;

    private String mContent;
    private boolean mHasComment = false;
    private int mLineNumber;

    enum TYPE {UNKNOWN, OBJECT, ARRAY, NUMBER, STRING, KEY, TOKEN}

    // @TODO: add description
    public static CLObject parse(String string) throws CLParsingException {
        return new CLParser(string).parse();
    }

    public CLParser(String content) {
        mContent = content;
    }

    // @TODO: add description
    public CLObject parse() throws CLParsingException {
        @SuppressWarnings("unused") CLObject root = null;

        char[] content = mContent.toCharArray();
        @SuppressWarnings("unused") CLElement currentElement = null;

        final int length = content.length;

        // First, let's find the root element start
        mLineNumber = 1;

        int startIndex = -1;
        for (int i = 0; i < length; i++) {
            char c = content[i];
            if (c == '{') {
                startIndex = i;
                break;
            }
            if (c == '\n') {
                mLineNumber++;
            }
        }
        if (startIndex == -1) {
            throw new CLParsingException("invalid json content", null);
        }

        // We have a root object, let's start
        root = CLObject.allocate(content);
        root.setLine(mLineNumber);
        root.setStart(startIndex);
        currentElement = root;

        for (int i = startIndex + 1; i < length; i++) {
            char c = content[i];
            if (c == '\n') {
                mLineNumber++;
            }
            if (mHasComment) {
                if (c == '\n') {
                    mHasComment = false;
                } else {
                    continue;
                }
            }
            if (false) {
                System.out.println("Looking at " + i + " : <" + c + ">");
            }
            if (currentElement == null) {
                break;
            }
            if (currentElement.isDone()) {
                currentElement = getNextJsonElement(i, c, currentElement, content);
            } else if (currentElement instanceof CLObject) {
                if (c == '}') {
                    currentElement.setEnd(i - 1);
                } else {
                    currentElement = getNextJsonElement(i, c, currentElement, content);
                }
            } else if (currentElement instanceof CLArray) {
                if (c == ']') {
                    currentElement.setEnd(i - 1);
                } else {
                    currentElement = getNextJsonElement(i, c, currentElement, content);
                }
            } else if (currentElement instanceof CLString) {
                char ck = content[(int) currentElement.mStart];
                if (ck == c) {
                    currentElement.setStart(currentElement.mStart + 1);
                    currentElement.setEnd(i - 1);
                }
            } else {
                if (currentElement instanceof CLToken) {
                    CLToken token = (CLToken) currentElement;
                    if (!token.validate(c, i)) {
                        throw new CLParsingException("parsing incorrect token " + token.content()
                                + " at line " + mLineNumber, token);
                    }
                }
                if (currentElement instanceof CLKey || currentElement instanceof CLString) {
                    char ck = content[(int) currentElement.mStart];
                    if ((ck == '\'' || ck == '"') && ck == c) {
                        currentElement.setStart(currentElement.mStart + 1);
                        currentElement.setEnd(i - 1);
                    }
                }
                if (!currentElement.isDone()) {
                    if (c == '}' || c == ']' || c == ',' || c == ' '
                            || c == '\t' || c == '\r' || c == '\n' || c == ':') {
                        currentElement.setEnd(i - 1);
                        if (c == '}' || c == ']') {
                            currentElement = currentElement.getContainer();
                            currentElement.setEnd(i - 1);
                            if (currentElement instanceof CLKey) {
                                currentElement = currentElement.getContainer();
                                currentElement.setEnd(i - 1);
                            }
                        }
                    }
                }
            }

            if (currentElement.isDone() && (!(currentElement instanceof CLKey)
                    || ((CLKey) currentElement).mElements.size() > 0)) {
                currentElement = currentElement.getContainer();
            }
        }

        // Close all open elements --
        // allow us to be more resistant to invalid json, useful during editing.
        while (currentElement != null && !currentElement.isDone()) {
            if (currentElement instanceof CLString) {
                currentElement.setStart((int) currentElement.mStart + 1);
            }
            currentElement.setEnd(length - 1);
            currentElement = currentElement.getContainer();
        }

        if (sDebug) {
            System.out.println("Root: " + root.toJSON());
        }

        return root;
    }

    private CLElement getNextJsonElement(int position, char c, CLElement currentElement,
            char[] content) throws CLParsingException {
        switch (c) {
            case ' ':
            case ':':
            case ',':
            case '\t':
            case '\r':
            case '\n': {
                // skip space
            }
            break;
            case '{': {
                currentElement = createElement(currentElement,
                        position, TYPE.OBJECT, true, content);
            }
            break;
            case '[': {
                currentElement = createElement(currentElement,
                        position, TYPE.ARRAY, true, content);
            }
            break;
            case ']':
            case '}': {
                currentElement.setEnd(position - 1);
                currentElement = currentElement.getContainer();
                currentElement.setEnd(position);
            }
            break;
            case '"':
            case '\'': {
                if (currentElement instanceof CLObject) {
                    currentElement = createElement(currentElement,
                            position, TYPE.KEY, true, content);
                } else {
                    currentElement = createElement(currentElement,
                            position, TYPE.STRING, true, content);
                }
            }
            break;
            case '/': {
                if (position + 1 < content.length && content[position + 1] == '/') {
                    mHasComment = true;
                }
            }
            break;
            case '-':
            case '+':
            case '.':
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9': {
                currentElement = createElement(currentElement,
                        position, TYPE.NUMBER, true, content);
            }
            break;
            default: {
                if (currentElement instanceof CLContainer
                        && !(currentElement instanceof CLObject)) {
                    currentElement = createElement(currentElement,
                            position, TYPE.TOKEN, true, content);
                    CLToken token = (CLToken) currentElement;
                    if (!token.validate(c, position)) {
                        throw new CLParsingException("incorrect token <"
                                + c + "> at line " + mLineNumber, token);
                    }
                } else {
                    currentElement = createElement(currentElement,
                            position, TYPE.KEY, true, content);
                }
            }
        }
        return currentElement;
    }

    private CLElement createElement(CLElement currentElement, int position,
            TYPE type, boolean applyStart, char[] content) {
        CLElement newElement = null;
        if (sDebug) {
            System.out.println("CREATE " + type + " at " + content[position]);
        }
        switch (type) {
            case OBJECT: {
                newElement = CLObject.allocate(content);
                position++;
            }
            break;
            case ARRAY: {
                newElement = CLArray.allocate(content);
                position++;
            }
            break;
            case STRING: {
                newElement = CLString.allocate(content);
            }
            break;
            case NUMBER: {
                newElement = CLNumber.allocate(content);
            }
            break;
            case KEY: {
                newElement = CLKey.allocate(content);
            }
            break;
            case TOKEN: {
                newElement = CLToken.allocate(content);
            }
            break;
            default:
                break;
        }
        if (newElement == null) {
            return null;
        }
        newElement.setLine(mLineNumber);
        if (applyStart) {
            newElement.setStart(position);
        }
        if (currentElement instanceof CLContainer) {
            CLContainer container = (CLContainer) currentElement;
            newElement.setContainer(container);
        }
        return newElement;
    }

}