1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package androidx.constraintlayout.core.parser;
17 
18 import org.jspecify.annotations.NonNull;
19 
20 import java.util.Arrays;
21 import java.util.Objects;
22 
23 /**
24  * Base element to represent a piece of parsed Json.
25  */
26 public class CLElement implements Cloneable {
27 
28     private final char[] mContent;
29     protected long mStart = -1;
30     protected long mEnd = Long.MAX_VALUE;
31     protected CLContainer mContainer;
32     private int mLine;
33 
34     protected static int sMaxLine = 80; // Max number of characters before the formatter indents
35     protected static int sBaseIndent = 2; // default indentation value
36 
CLElement(char[] content)37     public CLElement(char[] content) {
38         mContent = content;
39     }
40 
41     // @TODO: add description
notStarted()42     public boolean notStarted() {
43         return mStart == -1;
44     }
45 
setLine(int line)46     public void setLine(int line) {
47         this.mLine = line;
48     }
49 
50     /**
51      * get the line Number
52      *
53      * @return return the line number this element was on
54      */
getLine()55     public int getLine() {
56         return mLine;
57     }
58 
setStart(long start)59     public void setStart(long start) {
60         this.mStart = start;
61     }
62 
63     /**
64      * The character index this element was started on
65      */
getStart()66     public long getStart() {
67         return this.mStart;
68     }
69 
70     /**
71      * The character index this element was ended on
72      */
getEnd()73     public long getEnd() {
74         return this.mEnd;
75     }
76 
77     // @TODO: add description
setEnd(long end)78     public void setEnd(long end) {
79         if (this.mEnd != Long.MAX_VALUE) {
80             return;
81         }
82         this.mEnd = end;
83         if (CLParser.sDebug) {
84             System.out.println("closing " + this.hashCode() + " -> " + this);
85         }
86         if (mContainer != null) {
87             mContainer.add(this);
88         }
89     }
90 
addIndent(StringBuilder builder, int indent)91     protected void addIndent(StringBuilder builder, int indent) {
92         for (int i = 0; i < indent; i++) {
93             builder.append(' ');
94         }
95     }
96 
97     @Override
toString()98     public String toString() {
99         if (mStart > mEnd || mEnd == Long.MAX_VALUE) {
100             return this.getClass() + " (INVALID, " + mStart + "-" + mEnd + ")";
101         }
102         String content = new String(mContent);
103         content = content.substring((int) mStart, (int) mEnd + 1);
104 
105         return getStrClass() + " (" + mStart + " : " + mEnd + ") <<" + content + ">>";
106     }
107 
getStrClass()108     protected String getStrClass() {
109         String myClass = this.getClass().toString();
110         return myClass.substring(myClass.lastIndexOf('.') + 1);
111     }
112 
getDebugName()113     protected String getDebugName() {
114         if (CLParser.sDebug) {
115             return getStrClass() + " -> ";
116         }
117         return "";
118     }
119 
120     // @TODO: add description
content()121     public String content() {
122         String content = new String(mContent);
123         // Handle empty string
124         if (content.length() < 1) {
125             return "";
126         }
127         if (mEnd == Long.MAX_VALUE || mEnd < mStart) {
128             return content.substring((int) mStart, (int) mStart + 1);
129         }
130         return content.substring((int) mStart, (int) mEnd + 1);
131     }
132 
133     /**
134      * Whether this element has any valid content defined.
135      * <p>
136      * The content is valid when {@link #content()} can be called without causing exceptions.
137      */
hasContent()138     public boolean hasContent() {
139         return mContent != null && mContent.length >= 1;
140     }
141 
isDone()142     public boolean isDone() {
143         return mEnd != Long.MAX_VALUE;
144     }
145 
setContainer(CLContainer element)146     public void setContainer(CLContainer element) {
147         mContainer = element;
148     }
149 
getContainer()150     public CLElement getContainer() {
151         return mContainer;
152     }
153 
isStarted()154     public boolean isStarted() {
155         return mStart > -1;
156     }
157 
toJSON()158     protected String toJSON() {
159         return "";
160     }
161 
toFormattedJSON(int indent, int forceIndent)162     protected String toFormattedJSON(int indent, int forceIndent) {
163         return "";
164     }
165 
166     // @TODO: add description
getInt()167     public int getInt() {
168         if (this instanceof CLNumber) {
169             return ((CLNumber) this).getInt();
170         }
171         return 0;
172     }
173 
174     // @TODO: add description
getFloat()175     public float getFloat() {
176         if (this instanceof CLNumber) {
177             return ((CLNumber) this).getFloat();
178         }
179         return Float.NaN;
180     }
181 
182     @Override
equals(Object o)183     public boolean equals(Object o) {
184         if (this == o) return true;
185         if (!(o instanceof CLElement)) return false;
186 
187         CLElement clElement = (CLElement) o;
188 
189         if (mStart != clElement.mStart) return false;
190         if (mEnd != clElement.mEnd) return false;
191         if (mLine != clElement.mLine) return false;
192         if (!Arrays.equals(mContent, clElement.mContent)) return false;
193         return Objects.equals(mContainer, clElement.mContainer);
194     }
195 
196     @Override
hashCode()197     public int hashCode() {
198         // Auto-generated with Intellij Action "equals() and hashcode()"
199         int result = Arrays.hashCode(mContent);
200         result = 31 * result + (int) (mStart ^ (mStart >>> 32));
201         result = 31 * result + (int) (mEnd ^ (mEnd >>> 32));
202         result = 31 * result + (mContainer != null ? mContainer.hashCode() : 0);
203         result = 31 * result + mLine;
204         return result;
205     }
206 
207     @Override
clone()208     public @NonNull CLElement clone() {
209         try {
210             return (CLElement) super.clone();
211         } catch (CloneNotSupportedException e) {
212             throw new AssertionError();
213         }
214     }
215 }