1 /*
  2  * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.  Oracle designates this
  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 package oracle.code.json;
 26 
 27 import java.util.Objects;
 28 
 29 final class JsonDocumentInfo  {
 30 
 31     // Access to the underlying JSON contents
 32     final char[] doc;
 33     // tokens array/index are finalized by JsonParser::parse
 34     final int[] tokens;
 35     int index;
 36     // For exception message on failure
 37     int line = 0;
 38     int lineStart = 0;
 39 
 40     JsonDocumentInfo(char[] in) {
 41         doc = in;
 42         tokens = new int[doc.length];
 43         index = 0;
 44     }
 45 
 46     // Convenience to walk a token during inflation
 47     boolean shouldWalkToken(char c) {
 48         return switch (c) {
 49             case '"', '{', '['  -> true;
 50             default -> false;
 51         };
 52     }
 53 
 54     // gets offset in the input from the array index
 55     int getOffset(int index) {
 56         Objects.checkIndex(index, this.index);
 57         return tokens[index];
 58     }
 59 
 60     // Json Boolean, Null, and Number have an end index that is 1 greater
 61     // If the root is a primitive JSON value, -1 is returned as there are no indices
 62     int nextIndex(int index) {
 63         if (index + 1 < this.index) {
 64             return index + 1;
 65         } else {
 66             return -1;
 67         }
 68     }
 69 
 70     // Json Array and Object have an end index that corresponds to the closing bracket
 71     int nextIndex(int startIdx, char startToken, char endToken) {
 72         var index = startIdx + 1;
 73         int depth = 0;
 74         while (index < this.index) {
 75             var c = charAtIndex(index);
 76             if (c == startToken) {
 77                 depth++;
 78             } else if (c == endToken) {
 79                 depth--;
 80             }
 81             if (depth < 0) {
 82                 break;
 83             }
 84             index++;
 85         }
 86         return index;
 87     }
 88 
 89     // for convenience
 90     char charAtIndex(int index) {
 91         return doc[getOffset(index)];
 92     }
 93 
 94     int getIndexCount() {
 95         return index;
 96     }
 97 
 98     int getEndOffset() {
 99         return doc.length;
100     }
101 
102     // gets the char at the specified offset in the input
103     char charAt(int offset) {
104         return doc[offset];
105     }
106 
107     // gets the substring at the specified start/end offsets in the input
108     String substring(int startOffset, int endOffset) {
109         return new String(doc, startOffset, endOffset - startOffset);
110     }
111 
112     // Utility method to compose parse exception message
113     String composeParseExceptionMessage(String message, int line, int lineStart, int offset) {
114         return message + ": (%s) at Row %d, Col %d."
115                 .formatted(substring(offset, Math.min(offset + 8, doc.length)), line, offset - lineStart);
116     }
117 }