oracledb 5.4.0 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1 -1849
- package/LICENSE.txt +8 -222
- package/NOTICE.txt +1 -1
- package/README.md +38 -30
- package/SECURITY.md +38 -0
- package/THIRD_PARTY_LICENSES.txt +1 -1
- package/build/Release/oracledb-6.0.0-darwin-x64.node +0 -0
- package/build/Release/oracledb-6.0.0-darwin-x64.node-buildinfo.txt +1 -0
- package/build/Release/oracledb-6.0.0-js-buildinfo.txt +1 -0
- package/build/Release/oracledb-6.0.0-linux-arm64.node +0 -0
- package/build/Release/oracledb-6.0.0-linux-arm64.node-buildinfo.txt +1 -0
- package/build/Release/oracledb-6.0.0-linux-x64.node +0 -0
- package/build/Release/oracledb-6.0.0-linux-x64.node-buildinfo.txt +1 -0
- package/build/Release/oracledb-6.0.0-win32-x64.node +0 -0
- package/build/Release/oracledb-6.0.0-win32-x64.node-buildinfo.txt +1 -0
- package/examples/README.md +113 -0
- package/examples/dbconfig.js +100 -0
- package/examples/example.js +156 -0
- package/index.js +26 -0
- package/lib/aqDeqOptions.js +146 -7
- package/lib/aqEnqOptions.js +59 -7
- package/lib/aqMessage.js +118 -7
- package/lib/aqQueue.js +242 -53
- package/lib/connection.js +1324 -465
- package/lib/constants.js +203 -0
- package/lib/dbObject.js +346 -32
- package/lib/errors.js +781 -0
- package/lib/impl/aqDeqOptions.js +197 -0
- package/lib/impl/aqEnqOptions.js +89 -0
- package/lib/impl/aqMessage.js +134 -0
- package/lib/impl/aqQueue.js +81 -0
- package/lib/impl/connection.js +529 -0
- package/lib/impl/dbObject.js +159 -0
- package/lib/impl/index.js +59 -0
- package/lib/impl/lob.js +85 -0
- package/lib/impl/pool.js +195 -0
- package/lib/impl/resultset.js +223 -0
- package/lib/impl/sodaCollection.js +174 -0
- package/lib/impl/sodaDatabase.js +81 -0
- package/lib/impl/sodaDocCursor.js +63 -0
- package/lib/impl/sodaDocument.js +98 -0
- package/lib/impl/sodaOperation.js +109 -0
- package/lib/lob.js +156 -68
- package/lib/oracledb.js +1051 -257
- package/lib/pool.js +654 -465
- package/lib/poolStatistics.js +23 -9
- package/lib/queryStream.js +14 -7
- package/lib/resultset.js +292 -171
- package/lib/settings.js +191 -0
- package/lib/sodaCollection.js +214 -201
- package/lib/sodaDatabase.js +107 -71
- package/lib/sodaDocCursor.js +44 -32
- package/lib/sodaDocument.js +76 -14
- package/lib/sodaOperation.js +131 -118
- package/lib/thin/connection.js +1038 -0
- package/lib/thin/dbObject.js +629 -0
- package/lib/thin/index.js +40 -0
- package/lib/thin/lob.js +188 -0
- package/lib/thin/pool.js +481 -0
- package/lib/thin/protocol/buffer.js +1040 -0
- package/lib/thin/protocol/capabilities.js +116 -0
- package/lib/thin/protocol/constants.js +764 -0
- package/lib/thin/protocol/encryptDecrypt.js +185 -0
- package/lib/thin/protocol/messages/auth.js +348 -0
- package/lib/thin/protocol/messages/base.js +475 -0
- package/lib/thin/protocol/messages/commit.js +56 -0
- package/lib/thin/protocol/messages/dataType.js +382 -0
- package/lib/thin/protocol/messages/execute.js +312 -0
- package/lib/thin/protocol/messages/fetch.js +60 -0
- package/lib/thin/protocol/messages/index.js +53 -0
- package/lib/thin/protocol/messages/lobOp.js +200 -0
- package/lib/thin/protocol/messages/logOff.js +53 -0
- package/lib/thin/protocol/messages/ping.js +56 -0
- package/lib/thin/protocol/messages/protocol.js +78 -0
- package/lib/thin/protocol/messages/rollback.js +56 -0
- package/lib/thin/protocol/messages/sessionRelease.js +60 -0
- package/lib/thin/protocol/messages/withData.js +811 -0
- package/lib/thin/protocol/oson.js +660 -0
- package/lib/thin/protocol/packet.js +520 -0
- package/lib/thin/protocol/protocol.js +202 -0
- package/lib/thin/protocol/utils.js +88 -0
- package/lib/thin/resultSet.js +122 -0
- package/lib/thin/sqlnet/connStrategy.js +175 -0
- package/lib/thin/sqlnet/constants.js +199 -0
- package/lib/thin/sqlnet/ezConnectResolver.js +488 -0
- package/lib/thin/sqlnet/navNodes.js +870 -0
- package/lib/thin/sqlnet/networkSession.js +620 -0
- package/lib/thin/sqlnet/ntTcp.js +525 -0
- package/lib/thin/sqlnet/nvStrToNvPair.js +783 -0
- package/lib/thin/sqlnet/packet.js +405 -0
- package/lib/thin/sqlnet/paramParser.js +205 -0
- package/lib/thin/sqlnet/sessionAtts.js +160 -0
- package/lib/thin/statement.js +301 -0
- package/lib/thin/util.js +289 -0
- package/lib/transformer.js +250 -0
- package/lib/types.js +357 -0
- package/lib/util.js +194 -108
- package/lib/version.js +36 -0
- package/package/README.md +33 -30
- package/package/install.js +89 -64
- package/package/prunebinaries.js +60 -12
- package/package.json +3 -3
- package/build/Release/oracledb-5.4.0-darwin-x64.node +0 -0
- package/build/Release/oracledb-5.4.0-darwin-x64.node-buildinfo.txt +0 -1
- package/build/Release/oracledb-5.4.0-linux-x64.node +0 -0
- package/build/Release/oracledb-5.4.0-linux-x64.node-buildinfo.txt +0 -1
- package/build/Release/oracledb-5.4.0-win32-x64.node +0 -0
- package/build/Release/oracledb-5.4.0-win32-x64.node-buildinfo.txt +0 -1
|
@@ -0,0 +1,783 @@
|
|
|
1
|
+
// Copyright (c) 2022, 2023, Oracle and/or its affiliates.
|
|
2
|
+
|
|
3
|
+
//-----------------------------------------------------------------------------
|
|
4
|
+
//
|
|
5
|
+
// This software is dual-licensed to you under the Universal Permissive License
|
|
6
|
+
// (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
|
|
7
|
+
// 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
|
|
8
|
+
// either license.
|
|
9
|
+
//
|
|
10
|
+
// If you elect to accept the software under the Apache License, Version 2.0,
|
|
11
|
+
// the following applies:
|
|
12
|
+
//
|
|
13
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
14
|
+
// you may not use this file except in compliance with the License.
|
|
15
|
+
// You may obtain a copy of the License at
|
|
16
|
+
//
|
|
17
|
+
// https://www.apache.org/licenses/LICENSE-2.0
|
|
18
|
+
//
|
|
19
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
20
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
21
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
22
|
+
// See the License for the specific language governing permissions and
|
|
23
|
+
// limitations under the License.
|
|
24
|
+
//
|
|
25
|
+
//-----------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
'use strict';
|
|
28
|
+
|
|
29
|
+
const errors = require("../../errors.js");
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Constant which implies that the VALUE has not been set to ATOM or LIST.
|
|
34
|
+
*/
|
|
35
|
+
const RHS_NONE = 0;
|
|
36
|
+
/**
|
|
37
|
+
* Constant which implies that the VALUE of an NVPair is an ATOM.
|
|
38
|
+
*/
|
|
39
|
+
const RHS_ATOM = 1;
|
|
40
|
+
/**
|
|
41
|
+
* Constant which indicates that the VALUE of an NVPair is a list of NVPairs.
|
|
42
|
+
*/
|
|
43
|
+
const RHS_LIST = 2;
|
|
44
|
+
/**
|
|
45
|
+
* The List is in a regular format, i.e. (Name = Value) or (Name =
|
|
46
|
+
* (Name = Value)), and so on ..
|
|
47
|
+
*/
|
|
48
|
+
const LIST_REGULAR = 3;
|
|
49
|
+
/**
|
|
50
|
+
* The List is comma separated and looks like ( Name = Value, Value, Value )
|
|
51
|
+
*/
|
|
52
|
+
const LIST_COMMASEP = 4;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* An NVPair, or Name-Value Pair, is the structure used by SQL*Net to store
|
|
56
|
+
* address information. An example of an NV-Pair is:
|
|
57
|
+
*
|
|
58
|
+
* CID = (ADDRESS = (PROTOCOL = TCP)(HOST = XYZ)(PORT = 1521))
|
|
59
|
+
*
|
|
60
|
+
* Here is a (brief) description of the syntax:
|
|
61
|
+
*
|
|
62
|
+
* NVPair -> ( name = value ) value = atom | NVList
|
|
63
|
+
*/
|
|
64
|
+
|
|
65
|
+
class NVPair {
|
|
66
|
+
constructor(name) {
|
|
67
|
+
this.name = name;
|
|
68
|
+
this.listType = LIST_REGULAR;
|
|
69
|
+
this.rhsType = RHS_NONE;
|
|
70
|
+
}
|
|
71
|
+
set setAtom(atom) {
|
|
72
|
+
if (this._containsComment(atom)) {
|
|
73
|
+
errors.throwErr(errors.ERR_INVALID_CONNECT_STRING_SYNTAX);
|
|
74
|
+
}
|
|
75
|
+
this.rhsType = RHS_ATOM;
|
|
76
|
+
this.list = null;
|
|
77
|
+
this.atom = atom;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Checks if the input string contains comment.
|
|
81
|
+
* @param {string} str - input string
|
|
82
|
+
* @returns {boolean}
|
|
83
|
+
*/
|
|
84
|
+
_containsComment(str) {
|
|
85
|
+
for (let i = 0; i < str.length; i++) {
|
|
86
|
+
if (str.charAt(i) == '#') {
|
|
87
|
+
if (i != 0) {
|
|
88
|
+
// Check if this character is escaped
|
|
89
|
+
if (str.charAt(i - 1) == '\\')
|
|
90
|
+
continue;
|
|
91
|
+
else
|
|
92
|
+
return true;
|
|
93
|
+
} else {
|
|
94
|
+
// Entire line is a comment
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* gets the size of the list.
|
|
104
|
+
* @returns {integer}
|
|
105
|
+
*/
|
|
106
|
+
getListSize() {
|
|
107
|
+
if (this.list == null)
|
|
108
|
+
return 0;
|
|
109
|
+
else
|
|
110
|
+
return this.list.length;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* gets the element at a given position in the list.
|
|
114
|
+
* @param {integer} pos
|
|
115
|
+
* @returns {string}
|
|
116
|
+
*/
|
|
117
|
+
getListElement(pos) {
|
|
118
|
+
if (this.list == null)
|
|
119
|
+
return null;
|
|
120
|
+
else
|
|
121
|
+
return this.list[pos];
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* adds a nvpair to the existing one.
|
|
125
|
+
* @param {nvpair} pair
|
|
126
|
+
*/
|
|
127
|
+
addListElement(pair) {
|
|
128
|
+
if (this.list == null) {
|
|
129
|
+
this.rhsType = RHS_LIST;
|
|
130
|
+
this.list = new Array();
|
|
131
|
+
this.atom = null;
|
|
132
|
+
}
|
|
133
|
+
this.list.push(pair);
|
|
134
|
+
pair.parent = this;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* removes an element at a given position.
|
|
138
|
+
* @param {integer} pos
|
|
139
|
+
*/
|
|
140
|
+
removeListElement(pos) {
|
|
141
|
+
if (this.list != null) {
|
|
142
|
+
this.list.splice(pos, 1);
|
|
143
|
+
if (this.getListSize == 0) {
|
|
144
|
+
this.list = null;
|
|
145
|
+
this.rhsType = RHS_NONE;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Returns an empty string with the number specified in the argument. Used for
|
|
151
|
+
* indentation of multi-level NVPairs as they are stored
|
|
152
|
+
*
|
|
153
|
+
* @param count
|
|
154
|
+
* Number of spaces required in the blank string.
|
|
155
|
+
*/
|
|
156
|
+
_space(count) {
|
|
157
|
+
var blank_str = "";
|
|
158
|
+
for (let i = 0;i < count;i++) {
|
|
159
|
+
blank_str += " ";
|
|
160
|
+
}
|
|
161
|
+
return blank_str;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Returns the value of an NVPair (and all child NVPairs) as a readable
|
|
166
|
+
* String.
|
|
167
|
+
*/
|
|
168
|
+
valueToString() {
|
|
169
|
+
let out = "";
|
|
170
|
+
if (this.rhsType == RHS_ATOM) {
|
|
171
|
+
out = out + this.atom;
|
|
172
|
+
} else if (this.rhsType == RHS_LIST) {
|
|
173
|
+
if (this.listType == LIST_REGULAR) {
|
|
174
|
+
for (let i = 0; i < this.getListSize(); i++) {
|
|
175
|
+
out = out + this.getListElement(i).toString();
|
|
176
|
+
}
|
|
177
|
+
} else if (this.listType == LIST_COMMASEP) {
|
|
178
|
+
for (let i = 0; i < this.getListSize(); i++) {
|
|
179
|
+
let listElem = this.getListElement(i);
|
|
180
|
+
out = out + listElem.name;
|
|
181
|
+
if (i != this.getListSize() - 1)
|
|
182
|
+
out = out + ", ";
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return out;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
*
|
|
191
|
+
* @returns string representation of the nvpair
|
|
192
|
+
*/
|
|
193
|
+
toString() {
|
|
194
|
+
let out = "(" + this.name + "=";
|
|
195
|
+
if (this.rhsType == RHS_ATOM) {
|
|
196
|
+
out = out + this.atom;
|
|
197
|
+
} else if (this.rhsType == RHS_LIST) {
|
|
198
|
+
if (this.listType == LIST_REGULAR) {
|
|
199
|
+
for (let i = 0; i < this.getListSize(); i++) {
|
|
200
|
+
out = out + this.getListElement(i).toString();
|
|
201
|
+
}
|
|
202
|
+
} else if (this.listType == LIST_COMMASEP) {
|
|
203
|
+
out = out + " (";
|
|
204
|
+
for (let i = 0; i < this.getListSize(); i++) {
|
|
205
|
+
let listElem = this.getListElement(i);
|
|
206
|
+
out = out + listElem.name;
|
|
207
|
+
|
|
208
|
+
if (i != this.getListSize() - 1)
|
|
209
|
+
out = out + ", ";
|
|
210
|
+
}
|
|
211
|
+
out = out + ")";
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
out = out + ")";
|
|
215
|
+
return out;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Constant which indicates that there are no more tokens left.
|
|
222
|
+
*/
|
|
223
|
+
const TKN_NONE = 0;
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Constant for left parenthesis '(' token.
|
|
227
|
+
*/
|
|
228
|
+
const TKN_LPAREN = 1;
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Constant for right parenthesis ')' token.
|
|
232
|
+
*/
|
|
233
|
+
const TKN_RPAREN = 2;
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Constant for comma token ',' token.
|
|
237
|
+
*/
|
|
238
|
+
const TKN_COMMA = 3;
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Constant for equal sign '=' token.
|
|
242
|
+
*/
|
|
243
|
+
const TKN_EQUAL = 4;
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Constant for literal token.
|
|
247
|
+
*/
|
|
248
|
+
const TKN_LITERAL = 8;
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Constant marking end of NVString.
|
|
252
|
+
*/
|
|
253
|
+
const TKN_EOS = 9;
|
|
254
|
+
|
|
255
|
+
/*
|
|
256
|
+
* Characters used for comparison for tokens. When the analyzer hits and
|
|
257
|
+
* unescaped TKN_LPAREN_VALUE it interprets it as a TKN_LPAREN token.
|
|
258
|
+
*/
|
|
259
|
+
const TKN_LPAREN_VALUE = '(';
|
|
260
|
+
const TKN_RPAREN_VALUE = ')';
|
|
261
|
+
const TKN_COMMA_VALUE = ',';
|
|
262
|
+
const TKN_EQUAL_VALUE = '=';
|
|
263
|
+
const TKN_BKSLASH_VALUE = '\\';
|
|
264
|
+
const TKN_DQUOTE_VALUE = "\"";
|
|
265
|
+
const TKN_SQUOTE_VALUE = '\'';
|
|
266
|
+
const TKN_EOS_VALUE = '%';
|
|
267
|
+
|
|
268
|
+
/*
|
|
269
|
+
* Characters which are considered whitespace.
|
|
270
|
+
*/
|
|
271
|
+
const TKN_SPC_VALUE = ' ';
|
|
272
|
+
const TKN_TAB_VALUE = '\t';
|
|
273
|
+
const TKN_LF_VALUE = '\n';
|
|
274
|
+
const TKN_CR_VALUE = '\r';
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* The NVTokens class is used to help break NVStrings apart into tokens, such as
|
|
278
|
+
* TKN_LPAREN or TKN_LITERAL - this helps simplify the task of building NVPairs
|
|
279
|
+
* from an NVString.
|
|
280
|
+
*/
|
|
281
|
+
class NVTokens {
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Constructs NVTokens object for use.
|
|
286
|
+
*/
|
|
287
|
+
constructor() {
|
|
288
|
+
this.tkType = null;
|
|
289
|
+
this.tkValue = null;
|
|
290
|
+
this.numTokens = 0;
|
|
291
|
+
this.tkPos = 0;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/*
|
|
295
|
+
* function to determine if a given character is whitespace. The
|
|
296
|
+
* following constitute whitespace: ' ' (SPACE), '\t' (TAB), '\n' (NEWLINE),
|
|
297
|
+
* '\r' (LINEFEED),
|
|
298
|
+
*/
|
|
299
|
+
_isWhiteSpace(it) {
|
|
300
|
+
if ((it == TKN_SPC_VALUE) || (it == TKN_TAB_VALUE) || (it == TKN_LF_VALUE)
|
|
301
|
+
|| (it == TKN_CR_VALUE)) {
|
|
302
|
+
return true;
|
|
303
|
+
}
|
|
304
|
+
return false;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/*
|
|
308
|
+
* function to trim leading and trailing spaces from a literal.
|
|
309
|
+
*/
|
|
310
|
+
_trimWhiteSpace(it) {
|
|
311
|
+
let length = it.length;
|
|
312
|
+
let start = 0;
|
|
313
|
+
let end = length;
|
|
314
|
+
|
|
315
|
+
// Find first non-whitespace character
|
|
316
|
+
while ((start < length) && (this._isWhiteSpace(it.charAt(start)))) {
|
|
317
|
+
start++;
|
|
318
|
+
}
|
|
319
|
+
// From the back, find last non-whitespace character
|
|
320
|
+
while ((start < end) && (this._isWhiteSpace(it.charAt(end - 1)))) {
|
|
321
|
+
end--;
|
|
322
|
+
}
|
|
323
|
+
return it.substring(start, end);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Parses an NVString into a list of tokens which can be more easily
|
|
328
|
+
* interpreted. The list of tokens is stored within the class and must be
|
|
329
|
+
* accessed through getToken()/getLiteral() and eatToken().
|
|
330
|
+
*
|
|
331
|
+
* @param nvString
|
|
332
|
+
* NVString to be parsed.
|
|
333
|
+
*/
|
|
334
|
+
parseTokens(nvString) {
|
|
335
|
+
this.numTokens = 0;
|
|
336
|
+
this.tkPos = 0;
|
|
337
|
+
this.tkType = new Array();
|
|
338
|
+
this.tkValue = new Array();
|
|
339
|
+
|
|
340
|
+
let len = nvString.length;
|
|
341
|
+
let eql_seen = false;
|
|
342
|
+
// convert NVString to character array for easier access
|
|
343
|
+
let input = new Array();
|
|
344
|
+
input = Array.from(nvString);
|
|
345
|
+
let pos = 0; // position in NVString
|
|
346
|
+
|
|
347
|
+
while (pos < len) {
|
|
348
|
+
// eat leading whitespace
|
|
349
|
+
while ((pos < len) && (this._isWhiteSpace(input[pos]))) {
|
|
350
|
+
pos++;
|
|
351
|
+
}
|
|
352
|
+
if (pos < len) {
|
|
353
|
+
switch (input[pos]) {
|
|
354
|
+
// For metacharacters (, ), and =, add to the token list, and
|
|
355
|
+
// advance the NVString position. (Save token, eat character)
|
|
356
|
+
case TKN_LPAREN_VALUE:
|
|
357
|
+
eql_seen = false;
|
|
358
|
+
this._addToken(TKN_LPAREN, TKN_LPAREN_VALUE);
|
|
359
|
+
pos++;
|
|
360
|
+
break;
|
|
361
|
+
|
|
362
|
+
case TKN_EQUAL_VALUE:
|
|
363
|
+
eql_seen = true;
|
|
364
|
+
this._addToken(TKN_EQUAL, TKN_EQUAL_VALUE);
|
|
365
|
+
pos++;
|
|
366
|
+
break;
|
|
367
|
+
|
|
368
|
+
case TKN_RPAREN_VALUE:
|
|
369
|
+
eql_seen = false;
|
|
370
|
+
this._addToken(TKN_RPAREN, TKN_RPAREN_VALUE);
|
|
371
|
+
pos++;
|
|
372
|
+
break;
|
|
373
|
+
case TKN_COMMA_VALUE:
|
|
374
|
+
eql_seen = false;
|
|
375
|
+
this._addToken(TKN_COMMA, TKN_COMMA_VALUE);
|
|
376
|
+
pos++;
|
|
377
|
+
break;
|
|
378
|
+
|
|
379
|
+
default: // Otherwise, treat it as a literal
|
|
380
|
+
{
|
|
381
|
+
let startPos = pos;
|
|
382
|
+
let endPos = -1; // substring position in input
|
|
383
|
+
let quoted_str = false; // is literal wrapped with quotes?
|
|
384
|
+
let quote_char = TKN_DQUOTE_VALUE;
|
|
385
|
+
|
|
386
|
+
// does it begin with a single or double quote?
|
|
387
|
+
if ((input[pos] == TKN_SQUOTE_VALUE)
|
|
388
|
+
|| (input[pos] == TKN_DQUOTE_VALUE)) {
|
|
389
|
+
quoted_str = true;
|
|
390
|
+
quote_char = input[pos];
|
|
391
|
+
pos++;
|
|
392
|
+
startPos = pos;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
while (pos < len) {
|
|
396
|
+
// On a backslash (escaped character), save the backslash and
|
|
397
|
+
// following character into the literal.
|
|
398
|
+
if (input[pos] == TKN_BKSLASH_VALUE) {
|
|
399
|
+
pos += 2;
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
if (quoted_str) { // literal wrapped with quotes
|
|
404
|
+
if (input[pos] == quote_char) {// quote terminator found
|
|
405
|
+
pos++;
|
|
406
|
+
endPos = pos - 1; // exclusive
|
|
407
|
+
break;
|
|
408
|
+
}
|
|
409
|
+
} else { // did we hit unescaped meta character ( ) or =
|
|
410
|
+
if ((input[pos] == TKN_LPAREN_VALUE)
|
|
411
|
+
|| (input[pos] == TKN_RPAREN_VALUE)
|
|
412
|
+
|| ((input[pos] == TKN_COMMA_VALUE) && !eql_seen)
|
|
413
|
+
|| ((input[pos] == TKN_EQUAL_VALUE) && !eql_seen)) {
|
|
414
|
+
// terminate string - do NOT increment POS, or it will
|
|
415
|
+
// swallow the metacharacter into the literal
|
|
416
|
+
endPos = pos; // exclusive
|
|
417
|
+
break;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
pos++; // accept character into literal
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
if (endPos == -1) { // reached end of NVString without terminator
|
|
424
|
+
endPos = pos; // exclusive
|
|
425
|
+
}
|
|
426
|
+
this._addToken(TKN_LITERAL,
|
|
427
|
+
nvString.substring(startPos, endPos).trim());
|
|
428
|
+
break;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
// Add TKN_EOS as the last token in token list.
|
|
434
|
+
this._addToken(TKN_EOS, TKN_EOS_VALUE);
|
|
435
|
+
return true;
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Returns current token. Throws Error if no string has
|
|
439
|
+
* been parsed, or if there are no tokens left. Does NOT advance
|
|
440
|
+
* the token position.
|
|
441
|
+
*/
|
|
442
|
+
getToken() {
|
|
443
|
+
if (this.tkType == null) { // nothing parsed
|
|
444
|
+
errors.throwErr(errors.ERR_INVALID_CONNECT_STRING_SYNTAX);
|
|
445
|
+
}
|
|
446
|
+
if (this.tkPos < this.numTokens) {// are there tokens left?
|
|
447
|
+
return Number(this.tkType[this.tkPos]);
|
|
448
|
+
} else {
|
|
449
|
+
errors.throwErr(errors.ERR_INVALID_CONNECT_STRING_SYNTAX);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Returns current token. Throws Error if no string has
|
|
455
|
+
* been parsed, or if there are no tokens left. DOES advance the
|
|
456
|
+
* token position.
|
|
457
|
+
*/
|
|
458
|
+
popToken() {
|
|
459
|
+
let token = TKN_NONE;
|
|
460
|
+
|
|
461
|
+
if (this.tkType == null) {
|
|
462
|
+
errors.throwErr(errors.ERR_INVALID_CONNECT_STRING_SYNTAX);
|
|
463
|
+
}
|
|
464
|
+
if (this.tkPos < this.numTokens) { // if parsed and tokens left
|
|
465
|
+
token = Number(this.tkType[this.tkPos++]);
|
|
466
|
+
} else {
|
|
467
|
+
errors.throwErr(errors.ERR_INVALID_CONNECT_STRING_SYNTAX);
|
|
468
|
+
}
|
|
469
|
+
return token;
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Returns literal for current token. If current token is NOT a TKN_LITERAL,
|
|
473
|
+
* it returns a string representation of the current token. Throws
|
|
474
|
+
* Error if no string has been parsed, or
|
|
475
|
+
* if there are no tokens left.
|
|
476
|
+
*
|
|
477
|
+
* DOES NOT advance the token position.
|
|
478
|
+
*/
|
|
479
|
+
getLiteral() {
|
|
480
|
+
let theLiteral = null;
|
|
481
|
+
if (this.tkValue == null) {
|
|
482
|
+
errors.throwErr(errors.ERR_INVALID_CONNECT_STRING_SYNTAX);
|
|
483
|
+
}
|
|
484
|
+
// If we have parsed an NV string AND we have tokens left
|
|
485
|
+
if (this.tkPos < this.numTokens) {
|
|
486
|
+
theLiteral = String(this.tkValue[this.tkPos]);
|
|
487
|
+
} else {
|
|
488
|
+
errors.throwErr(errors.ERR_INVALID_CONNECT_STRING_SYNTAX);
|
|
489
|
+
}
|
|
490
|
+
return theLiteral;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Returns literal for current token. If current token is NOT a TKN_LITERAL,
|
|
495
|
+
* it returns a string representation of the current token. Throws
|
|
496
|
+
* Error if no string has been parsed, or
|
|
497
|
+
* if there are no tokens left.
|
|
498
|
+
*
|
|
499
|
+
* DOES advance the token position.
|
|
500
|
+
*/
|
|
501
|
+
popLiteral() {
|
|
502
|
+
let theLiteral = null;
|
|
503
|
+
|
|
504
|
+
if (this.tkValue == null) {
|
|
505
|
+
errors.throwErr(errors.ERR_INVALID_CONNECT_STRING_SYNTAX);
|
|
506
|
+
}
|
|
507
|
+
// If we have parsed an NV string AND we have tokens left.
|
|
508
|
+
if (this.tkPos < this.numTokens) {
|
|
509
|
+
theLiteral = String(this.tkValue[this.tkPos++]);
|
|
510
|
+
} else {
|
|
511
|
+
errors.throwErr(errors.ERR_INVALID_CONNECT_STRING_SYNTAX);
|
|
512
|
+
}
|
|
513
|
+
return theLiteral;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Advances the token position by one.
|
|
518
|
+
*/
|
|
519
|
+
eatToken() {
|
|
520
|
+
if (this.tkPos < this.numTokens) {
|
|
521
|
+
this.tkPos++;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* Returns NVTokens list as a readable String.
|
|
527
|
+
*/
|
|
528
|
+
toString() {
|
|
529
|
+
if (this.tkType == null) {
|
|
530
|
+
return "*NO TOKENS*";
|
|
531
|
+
}
|
|
532
|
+
let out = "Tokens";
|
|
533
|
+
for (let i = 0; i < this.numTokens; i++) {
|
|
534
|
+
out = out + " : " + this.tkValue[i];
|
|
535
|
+
}
|
|
536
|
+
return out;
|
|
537
|
+
}
|
|
538
|
+
/*
|
|
539
|
+
* function to add a token and corresponding printable version (i.e.,
|
|
540
|
+
* TKN_LPAREN and TKN_LPAREN_VALUE) into the token list.
|
|
541
|
+
*/
|
|
542
|
+
_addToken(tk, tk_val) {
|
|
543
|
+
this.tkType.push(Number(tk));
|
|
544
|
+
this.tkValue.push(String(tk_val));
|
|
545
|
+
this.numTokens++;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* The NVFactory class is used to help interpret the tokens generated by
|
|
554
|
+
* NVTokens from an NVString.
|
|
555
|
+
*/
|
|
556
|
+
|
|
557
|
+
|
|
558
|
+
/**
|
|
559
|
+
* Returns an NVPair which contains the broken-down form of nvString
|
|
560
|
+
* @param nvString the nvString to parse
|
|
561
|
+
*/
|
|
562
|
+
function createNVPair(nvString) {
|
|
563
|
+
let nvt = new NVTokens();
|
|
564
|
+
nvt.parseTokens(nvString);
|
|
565
|
+
return readTopLevelNVPair(nvt);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/*
|
|
569
|
+
* function which returns a top-level NVPair from NVTokens.
|
|
570
|
+
* NVPair: (name=value)
|
|
571
|
+
* value: atom | NVList
|
|
572
|
+
*/
|
|
573
|
+
function readTopLevelNVPair(nvt) {
|
|
574
|
+
//check for opening (
|
|
575
|
+
let tk = nvt.getToken();
|
|
576
|
+
nvt.eatToken();
|
|
577
|
+
if (tk != TKN_LPAREN) {
|
|
578
|
+
errors.throwErr(errors.ERR_INVALID_CONNECT_STRING_SYNTAX);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
let name = readNVLiteral(nvt);
|
|
582
|
+
let nvp = new NVPair(name);
|
|
583
|
+
|
|
584
|
+
if ((tk = nvt.getToken()) == TKN_COMMA) {
|
|
585
|
+
// read comma'ed names as one name
|
|
586
|
+
while (tk == TKN_LITERAL || tk == TKN_COMMA) {
|
|
587
|
+
name += nvt.popLiteral();
|
|
588
|
+
tk = nvt.getToken();
|
|
589
|
+
}
|
|
590
|
+
nvp.name = name;
|
|
591
|
+
|
|
592
|
+
return readRightHandSide(nvp, nvt);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
return readRightHandSide(nvp, nvt);
|
|
596
|
+
}
|
|
597
|
+
/*
|
|
598
|
+
* function which returns the next NVPair from NVTokens.
|
|
599
|
+
* NVPair: (name=value) | (name, | ,name) | ,name,
|
|
600
|
+
* value: atom | NVList
|
|
601
|
+
*/
|
|
602
|
+
function readNVPair(nvt) {
|
|
603
|
+
// Opening ( or , for NVPair
|
|
604
|
+
let tk = nvt.getToken();
|
|
605
|
+
nvt.eatToken();
|
|
606
|
+
if (!((tk == TKN_LPAREN) || (tk == TKN_COMMA))) {
|
|
607
|
+
errors.throwErr(errors.ERR_INVALID_CONNECT_STRING_SYNTAX);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
let name = readNVLiteral(nvt);
|
|
611
|
+
let nvp = new NVPair(name);
|
|
612
|
+
|
|
613
|
+
return readRightHandSide(nvp, nvt);
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/*
|
|
617
|
+
* function which reads rhs and returns NVPair from NVTokens.
|
|
618
|
+
* NVPair: (name=value)
|
|
619
|
+
* value: atom | NVList
|
|
620
|
+
*/
|
|
621
|
+
function readRightHandSide(nvp, nvt) {
|
|
622
|
+
let tk = nvt.getToken();
|
|
623
|
+
switch (tk) {
|
|
624
|
+
case TKN_EQUAL:
|
|
625
|
+
nvt.eatToken();
|
|
626
|
+
|
|
627
|
+
// If the next token after "=" is a LITERAL, then read an atom,
|
|
628
|
+
// otherwise read an NVList.
|
|
629
|
+
tk = nvt.getToken();
|
|
630
|
+
if (tk == TKN_LITERAL) {
|
|
631
|
+
let value = readNVLiteral(nvt);
|
|
632
|
+
nvp.setAtom = value;
|
|
633
|
+
} else {
|
|
634
|
+
// NVList is responsible for adding child NVPairs to this parent
|
|
635
|
+
// NVPair.
|
|
636
|
+
readNVList(nvt, nvp);
|
|
637
|
+
}
|
|
638
|
+
break;
|
|
639
|
+
|
|
640
|
+
case TKN_COMMA:
|
|
641
|
+
case TKN_RPAREN:
|
|
642
|
+
|
|
643
|
+
// If we get a "comma" or ")", then we need to parse a list of values.
|
|
644
|
+
// eg, "(x=(value1, value2,...))" or "(x=(value))"
|
|
645
|
+
nvp.setAtom = nvp.name;
|
|
646
|
+
break;
|
|
647
|
+
|
|
648
|
+
default:
|
|
649
|
+
errors.throwErr(errors.ERR_INVALID_CONNECT_STRING_SYNTAX);
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// terminating ")" or "," for NVPair
|
|
653
|
+
tk = nvt.getToken();
|
|
654
|
+
if (tk == TKN_RPAREN) {
|
|
655
|
+
nvt.eatToken();
|
|
656
|
+
} else if (tk != TKN_COMMA) {
|
|
657
|
+
errors.throwErr(errors.ERR_INVALID_CONNECT_STRING_SYNTAX);
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
return nvp;
|
|
661
|
+
}
|
|
662
|
+
/*
|
|
663
|
+
* function which returns the next literal from NVTokens.
|
|
664
|
+
*/
|
|
665
|
+
function readNVLiteral(nvt) {
|
|
666
|
+
let tk = nvt.getToken();
|
|
667
|
+
if (tk != TKN_LITERAL) {
|
|
668
|
+
errors.throwErr(errors.ERR_INVALID_CONNECT_STRING_SYNTAX);
|
|
669
|
+
}
|
|
670
|
+
return nvt.popLiteral();
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
/*
|
|
674
|
+
* function which adds a list of NVPairs to a parent NVPair.
|
|
675
|
+
* NVList: NVPair NVList | epsilon
|
|
676
|
+
*/
|
|
677
|
+
function readNVList(nvt, parent) {
|
|
678
|
+
// if next token is "(" or ",", then read an NVPair
|
|
679
|
+
// otherwise, assume epsilon
|
|
680
|
+
let tk = nvt.getToken();
|
|
681
|
+
if (!(tk == TKN_LPAREN || tk == TKN_COMMA)) {
|
|
682
|
+
return; // didn't read an nvpair
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
let child = readNVPair(nvt);
|
|
686
|
+
|
|
687
|
+
// read a good NVPair
|
|
688
|
+
parent.addListElement(child);
|
|
689
|
+
if ((tk == TKN_COMMA) || (child.name == child.atom)) {
|
|
690
|
+
if (parent.getListType != LIST_COMMASEP) // if not already set
|
|
691
|
+
parent.setListType = LIST_COMMASEP; // set it
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
readNVList(nvt, parent); // next iteration of NVList()
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
/**
|
|
698
|
+
* Returns a NVPair whose name matches (ignoring case) the specified
|
|
699
|
+
* name. This function does search recursively through all descendents
|
|
700
|
+
* of the specified NVPair.
|
|
701
|
+
* @param nvp NVPair to search through
|
|
702
|
+
* @param name name to match (ignoring case)
|
|
703
|
+
*/
|
|
704
|
+
function findNVPairRecurse(nvp, name) {
|
|
705
|
+
/* Is the base NV Pair the name we are looking for? */
|
|
706
|
+
if (!nvp) {
|
|
707
|
+
return null;
|
|
708
|
+
}
|
|
709
|
+
if ((name.toUpperCase() == (nvp.name).toUpperCase()))
|
|
710
|
+
return nvp;
|
|
711
|
+
|
|
712
|
+
/* Do we have anywhere else to search (ie, is nvp a list)? */
|
|
713
|
+
if (nvp.getRHSType == RHS_ATOM)
|
|
714
|
+
return null;
|
|
715
|
+
|
|
716
|
+
/* Loop thru the list of children and searching each child for name. */
|
|
717
|
+
for (let i = 0; i < nvp.getListSize(); i++) {
|
|
718
|
+
let child = findNVPairRecurse(nvp.getListElement(i), name);
|
|
719
|
+
|
|
720
|
+
/* Did we find "name"? */
|
|
721
|
+
if (child !== null)
|
|
722
|
+
return child;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
return null;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
/**
|
|
729
|
+
* Returns a NVPair whose name matches (ignoring case) the specified
|
|
730
|
+
* name. This functions only searches the direct descendants of specified NVPair
|
|
731
|
+
* @param nvp NVPair to search through
|
|
732
|
+
* @param name name to match (ignoring case)
|
|
733
|
+
*/
|
|
734
|
+
function findNVPair(nvp, name) {
|
|
735
|
+
if (!nvp) {
|
|
736
|
+
return null;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
/* Do we have anywhere else to search (ie, is nvp a list)? */
|
|
740
|
+
if (nvp.getRHSType == RHS_ATOM)
|
|
741
|
+
return null;
|
|
742
|
+
|
|
743
|
+
/* Loop thru the list of children and searching each child for name. */
|
|
744
|
+
for (let i = 0; i < nvp.getListSize(); i++) {
|
|
745
|
+
let child = nvp.getListElement(i);
|
|
746
|
+
if (name.toUpperCase() == (child.name).toUpperCase())
|
|
747
|
+
return child;
|
|
748
|
+
}
|
|
749
|
+
return null;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
/**
|
|
753
|
+
* Returns a value which matches the specified path
|
|
754
|
+
* @param nvp NVPair to search through
|
|
755
|
+
* @param name array of names to match (ignoring case)
|
|
756
|
+
*/
|
|
757
|
+
function findValue(nvp, names) {
|
|
758
|
+
if (!nvp) {
|
|
759
|
+
return null;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
/* Is the base NV Pair the first name in path */
|
|
763
|
+
if ((names[0].toUpperCase() != (nvp.name).toUpperCase()))
|
|
764
|
+
return null;
|
|
765
|
+
|
|
766
|
+
let output = nvp;
|
|
767
|
+
let sze = names.length;
|
|
768
|
+
|
|
769
|
+
for (let i = 1; i < sze; i++) {
|
|
770
|
+
output = findNVPair(output, names[i]);
|
|
771
|
+
if (!output)
|
|
772
|
+
return null;
|
|
773
|
+
}
|
|
774
|
+
if (output.atom == null) {
|
|
775
|
+
if (output.list == null)
|
|
776
|
+
return null;
|
|
777
|
+
else
|
|
778
|
+
return (output.list).toString();
|
|
779
|
+
} else {
|
|
780
|
+
return (output.atom).toString();
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
module.exports = {findNVPairRecurse, createNVPair, findNVPair, findValue};
|