1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
package Torello.Browser.JsonAST;

import Torello.JavaDoc.Annotations.JDHeaderBackgroundImg;

import Torello.Java.ReadOnly.ReadOnlyList;
import Torello.Java.ReadOnly.ReadOnlySet;

import Torello.Java.Additional.Ret2;
import Torello.Java.Additional.Ret4;

import Torello.Java.StrCSV;

import javax.json.JsonObject;
import javax.json.JsonArray;

/**
 * Root ancestor for all CDP JSON entities parsed from the protocol specs
 * (<CODE>browser_protocol&#46;json</CODE> and <CODE>js_protocol&#46;json</CODE>).
 * 
 * <EMBED CLASS='external-html' DATA-FILE-ID=AST_TREES>
 * <EMBED CLASS="external-html" DATA-FILE-ID=Entity>
 * 
 * @see TCE
 * @see PPR
 * @see PropName
 * @see TypeProp
 */
@JDHeaderBackgroundImg(EmbedTagFileID="AST_NODES_JDHBI")
public abstract class Entity implements java.io.Serializable, Comparable<Entity>
{
    // ********************************************************************************************
    // ********************************************************************************************
    // STATIC FINAL FIELDS
    // ********************************************************************************************
    // ********************************************************************************************


    /** <EMBED CLASS='external-html' DATA-FILE-ID=SVUID> */
    protected static final long serialVersionUID = 1;


    // ********************************************************************************************
    // ********************************************************************************************
    // Constant & Final Instance-Fields (Set by the Constructor)
    // ********************************************************************************************
    // ********************************************************************************************


    /**
     * Monotonic ID for this CDP entity used by {@code equals} and {@code hashCode}.
     * <EMBED CLASS='external-html' DATA-FILE-ID=Entity.id>
     */
    public final int id = IDManager.nextID();

    /**
     * CDP domain that owns this entity (for example, {@code 'Network'} or {@code 'DOM'}).
     * <EMBED CLASS='external-html' DATA-FILE-ID=Entity.ownerDomain>
     */
    public final Domain ownerDomain;

    /**
     * Canonical CDP name for this entity.
     * <EMBED CLASS='external-html' DATA-FILE-ID=Entity.name>
     */
    public final String name;

    /**
     * Human-readable description of the CDP entity as specified in the protocol, by Google.
     * <EMBED CLASS='external-html' DATA-FILE-ID=Entity.description>
     */
    public final String description;

    /**
     * There are only two concrete subclasses of {@code Entity}: {@link PPR} and {@TCE}.
     * <EMBED CLASS='external-html' DATA-FILE-ID=Entity.whichEntity>
     */
    public final WhichEntity whichEntity;

    /**
     * List of all {@code JsonObject} property keys for this CDP item, as parsed from the 
     * {@code JsonObjet} from which this {@code Entity} instance was extracted.
     * <EMBED CLASS='external-html' DATA-FILE-ID=Entity.propNames>
     */
    public final ReadOnlyList<PropName> propNames;

    /**
     * The {@link JsonObject} {@code "type"} Property for this item as a strict enum.
     * <EMBED CLASS='external-html' DATA-FILE-ID=Entity.typeProp>
     */
    public final TypeProp typeProp;

    /**
     * Additional type, used when the {@link #typeProp} equals {@link TypeProp#ARRAY}.
     * <EMBED CLASS='external-html' DATA-FILE-ID=Entity.arrItemsTypeProp>
     */
    public final TypeProp arrItemsTypeProp;

    /**
     * True if this CDP item is optional.
     * <EMBED CLASS='external-html' DATA-FILE-ID=Entity.optional>
     */
    public final boolean optional;

    /**
     * True if this CDP item is marked experimental.
     * <EMBED CLASS='external-html' DATA-FILE-ID=Entity.experimental>
     */
    public final boolean experimental;

    /**
     * True if this CDP item is marked deprecated.
     * <EMBED CLASS='external-html' DATA-FILE-ID=Entity.deprecated>
     */
    public final boolean deprecated;

    /**
     * Allowed enumeration {@code String} values for this CDP item.
     * <EMBED CLASS='external-html' DATA-FILE-ID=Entity.enumVals>
     */
    public final ReadOnlyList<String> enumVals;

    /**
     * Comma-separated rendering of {@link #enumVals}, used to facilitate printing summaries.
     * <EMBED CLASS='external-html' DATA-FILE-ID=Entity.enumValsStr>
     */
    public final String enumValsStr;

    /**
     * Every {@code Entity} instance is parsed from a {@link JsonArray}; this is the array index.
     * <EMBED CLASS='external-html' DATA-FILE-ID=Entity.index>
     */
    public final int index;


    // ********************************************************************************************
    // ********************************************************************************************
    // Constructor
    // ********************************************************************************************
    // ********************************************************************************************


    Entity(
            final Domain                ownerDomain,
            final WhichEntity           which,
            final JsonObject            jo,
            final int                   index,
            final ReadOnlySet<String>   extraAllowedJsonPropNames
        )
    {
        this.ownerDomain    = ownerDomain;
        this.whichEntity    = which;
        this.index          = index;


        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // The complete list of Json-Properties contained by this object (at the top level)
        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // 
        // This is used inside the "exception location summry".  This must be done first..
        this.name = Helper$Name.getName(this, jo);


        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // JsonException - Expected Keys
        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // 
        // Make sure we have looked at all the elements in the JsonObject
        // If there are any we missed, throw an exception.

        Helper$CheckKeys.entityCheck(this, jo, extraAllowedJsonPropNames);


        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // The complete list of Json-Properties contained by this object (at the top level)
        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

        this.propNames = Helper$Misc.getPropNames(jo);


        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // typeProp & arrItemsTypeProp
        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

        Ret2<TypeProp, TypeProp> ret2 = Helper$TypeProps.get(jo);

        this.typeProp           = ret2.a;
        this.arrItemsTypeProp   = ret2.b;


        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // this.description & FLAGS
        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

        final Ret4<String, Boolean, Boolean, Boolean> ret4 = Helper$Misc.getDescAndFlags(jo);

        this.description    = ret4.a;   
        this.optional       = ret4.b;
        this.experimental   = ret4.c;
        this.deprecated     = ret4.d;


        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // An 'enum' property in the JsonObject / Entity
        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

        this.enumVals = Helper$Enum.getEnumPropertyOrNull(this, jo);

        this.enumValsStr = (enumVals == null)
            ? null
            : StrCSV.toCSV(enumVals, (String s) -> ("\"" + s + "\""), true, null);
    }

    /** Helper method, used to print this node's vital information. */
    public final String exceptionLocationSummary()
    {
        return
            "Node-Name:       [" + this.name + "]\n" +
            "Owner-Domain:    [" + this.ownerDomain.name + "]\n" +
            "WhichEntity:     [" + this.whichEntity + "]\n" +
            "Class-Name:      [" + this.getClass().getSimpleName() + "]\n" +
            "JsonArray Index: [" + this.index + "]\n";
    }


    // The Type-Parameter "T" is a lesser-known trick to get around a (somewhat glaring) JDK / 
    // 'javac' bug.  Initializing final variables mandates that all Code-Execution Paths assign 
    // some value to a final variable!  This is required either inside of a constructor, and also
    // for local final variables too!  However, if one Code Execution Path ends with a method that
    // is clearly guaranteed to throw an exception - as below - then 'javac' will flag an error 
    // saying that "variable [XXX] might not have been initialized."
    // 
    // Place the word "throw" in front of the call to this method, and it will prevent that
    // complaint.

    final <T extends Throwable> T verifyThrow(final String message)
    {
        throw new ASTError
            (message + '\n' + this.toString() + '\n' + this.exceptionLocationSummary());
    }


    // ********************************************************************************************
    // ********************************************************************************************
    // Comparable, Object
    // ********************************************************************************************
    // ********************************************************************************************


    /** Uses several pieces of information for generating a comparison value. */
    public final int compareTo(final Entity other)
    {
        if (this == other) return 0;
        if (other == null) return 1;

        if (this.ownerDomain.ownerAPI != other.ownerDomain.ownerAPI)
            return this.ownerDomain.ownerAPI.name.compareTo
                (other.ownerDomain.ownerAPI.name);

        if (this.ownerDomain != other.ownerDomain)
            return this.ownerDomain.name.compareTo(other.ownerDomain.name);

        if (this.whichEntity != other.whichEntity)
            return this.whichEntity.compareTo(other.whichEntity);

        int c = this.name.compareTo(other.name);

        return (c != 0)
            ? c
            : Integer.compare(this.hashCode(), other.hashCode());
    }

    /** Returns the {@link #id} as a hash value. */
    public final int hashCode()
    { return id; }

    /** Checks that this {@link #id} is equal to {@code other.id}. */
    public final boolean equals(final Object other)
    {
        if (other == null)              return false;
        if (!(other instanceof Entity)) return false;
        return this.id == ((Entity) other).id;
    }
}