com.pivotal.gemfirexd.internal.iapi.util.IdUtil Maven / Gradle / Ivy
Show all versions of snappydata-store-core Show documentation
/*
Derby - Class com.ihost.cs.IdUtil
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to you 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.
*/
/*
* Changes for GemFireXD distributed data platform (some marked by "GemStone changes")
*
* Portions Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
*
* 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. See accompanying
* LICENSE file.
*/
package com.pivotal.gemfirexd.internal.iapi.util;
import com.pivotal.gemfirexd.internal.iapi.error.StandardException;
import com.pivotal.gemfirexd.internal.iapi.reference.Attribute;
import com.pivotal.gemfirexd.internal.iapi.reference.Property;
import com.pivotal.gemfirexd.internal.iapi.reference.SQLState;
import java.io.IOException;
import java.io.StringReader;
import java.util.Vector;
import java.util.HashSet;
import java.util.Properties;
/**
Utility class for parsing and producing string representations of
ids. This class supports both delimited and un-delimited ids.
The syntax for an id follows.
id := delim-id | unDelim-id
delim-id := "[""|[any char but quote]]+"
undelim-id := (a-z|A-Z|anyunicodeletter)[a-z|A-Z|_|0-9|anyunicodeletter|anyunicodedigit]*
In the syntax braces show grouping. '*' means repeat 0 or more times.
'|' means or. '+' means repeat 1 or more times.
In addition this class provides support for qualified names. A qualified name
is a dot (.) separated list of ids.
Limitations:
- Unicode escape sequences in ids are not supported.
- Escape sequences (\n...) are not supported.
*/
public abstract class IdUtil
{
/**
* Produce a delimited form of a normal value.
@return the delimited identifier.
*/
public static String normalToDelimited(String id)
{
StringBuilder quotedBuffer = new StringBuilder();
quotedBuffer.append('\"');
for (int ix = 0; ix < id.length(); ix++){
char currentChar = id.charAt(ix);
quotedBuffer.append(currentChar);
if (currentChar == '\"')
quotedBuffer.append('\"');
}
quotedBuffer.append('\"');
return quotedBuffer.toString();
}
/**
Produce a delimited two part qualified name from two
un-delimited identifiers.
@return the result.
*/
public static String mkQualifiedName(String id1,
String id2)
{
if( null == id1)
return normalToDelimited(id2);
return
normalToDelimited(id1) +
"." +
normalToDelimited(id2);
}
/**
Make a string form of a qualified name from the array of ids provided.
*/
public static String mkQualifiedName(String[] ids)
{
StringBuilder sb = new StringBuilder();
for (int ix=0; ix < ids.length; ix++)
{
if (ix!=0) sb.append(".");
sb.append(normalToDelimited(ids[ix]));
}
return sb.toString();
}
/**
Parse a multi-part (dot separated) SQL identifier form the
String provided. Raise an excepion
if the string does not contain valid SQL indentifiers.
The returned String array contains the normalized form of the
identifiers.
@param s The string to be parsed
@return An array of strings made by breaking the input string at its dots, '.'.
@exception StandardException Oops
*/
public static String[] parseMultiPartSQLIdentifier(String s)
throws StandardException
{
StringReader r = new StringReader(s);
String[] qName = parseMultiPartSQLIdentifier(r);
verifyEmpty(r);
return qName;
}
/**
@param r The multi-part identifier to be parsed
@return An array of strings made by breaking the input string at its dots, '.'.
@exception StandardException Oops
*/
private static String[] parseMultiPartSQLIdentifier(StringReader r)
throws StandardException
{
Vector v = new Vector();
while (true)
{
String thisId = parseId(r,true);
v.addElement(thisId);
int dot;
try {
r.mark(0);
dot = r.read();
if (dot != '.')
{
if (dot!=-1) r.reset();
break;
}
}
catch (IOException ioe){
throw StandardException.newException(SQLState.ID_PARSE_ERROR,ioe);
}
}
String[] result = new String[v.size()];
v.copyInto(result);
return result;
}
/**
Parse a SQL identifier from the String provided. Raise an excepion
if the string does not contain a valid SQL indentifier.
The returned String contains the normalized form of the
identifier.
@exception StandardException Oops
*/
public static String parseSQLIdentifier(String s)
throws StandardException
{
StringReader r = new StringReader(s);
String id = parseId(r,true);
verifyEmpty(r);
return id;
}
/**
Read an id from the StringReader provided.
@param normalize true means return ids in nomral form, false means
return them as they were entered.
Raise an exception if the first thing in the StringReader
is not a valid id.
@exception StandardException Ooops.
*/
private static String parseId(StringReader r, boolean normalize)
throws StandardException
{
try {
r.mark(0);
int c = r.read();
if (c == -1) //id can't be 0-length
throw StandardException.newException(SQLState.ID_PARSE_ERROR);
r.reset();
if (c == '"')
return parseQId(r,normalize);
else
return parseUnQId(r,normalize);
}
catch (IOException ioe){
throw StandardException.newException(SQLState.ID_PARSE_ERROR,ioe);
}
}
/**
* Parse a regular identifier (unquoted) returning returning either
* the value of the identifier or a delimited identifier. Ensures
* that all characters in the identifer are valid for a regular identifier.
*
* @param r Regular identifier to parse.
* @param normalize If true return the identifer converted to a single case, otherwise return the identifier as entered.
* @return the value of the identifer or a delimited identifier
* @throws IOException Error accessing value
* @throws StandardException Error parsing identifier.
*/
private static String parseUnQId(StringReader r, boolean normalize)
throws IOException,StandardException
{
StringBuilder b = new StringBuilder();
int c;
boolean first;
//
for(first = true; ; first=false)
{
r.mark(0);
if (idChar(first,c=r.read()))
b.append((char)c);
else
break;
}
if (c != -1) r.reset();
String id = b.toString();
if (normalize)
return StringUtil.SQLToUpperCase(id);
else
return id;
}
private static boolean idChar(boolean first,int c)
{
if (((c>='a' && c<='z') || (c>='A' && c<='Z')) ||
(!first &&(c>='0' && c<='9')) || (!first &&c =='_') )
return true;
else if (Character.isLetter((char) c))
return true;
else if (!first && Character.isDigit((char) c))
return true;
return false;
}
/**
* Parse a delimited (quoted) identifier returning either
* the value of the identifier or a delimited identifier.
* @param r Quoted identifier to parse.
* @param normalize If true return a delimited identifer, otherwise return the identifier's value.
* @return the value of the identifer or a delimited identifier
* @throws IOException Error accessing value
* @throws StandardException Error parsing identifier.
*/
private static String parseQId(StringReader r,boolean normalize)
throws IOException,StandardException
{
StringBuilder b = new StringBuilder();
int c = r.read();
if (c != '"') throw StandardException.newException(SQLState.ID_PARSE_ERROR);
while (true)
{
c=r.read();
if (c == '"')
{
r.mark(0);
int c2 = r.read();
if (c2 != '"')
{
if (c2!=-1)r.reset();
break;
}
}
else if (c == -1)
throw StandardException.newException(SQLState.ID_PARSE_ERROR);
b.append((char)c);
}
if (b.length() == 0) //id can't be 0-length
throw StandardException.newException(SQLState.ID_PARSE_ERROR);
if (normalize)
return b.toString();
else
return normalToDelimited(b.toString()); //Put the quotes back.
}
/**
* Verify the read is empty (no more characters in its stream).
* @param r
* @throws StandardException
*/
private static void verifyEmpty(java.io.Reader r)
throws StandardException
{
try {
if (r.read() != -1)
throw StandardException.newException(SQLState.ID_PARSE_ERROR);
}
catch (IOException ioe){
throw StandardException.newException(SQLState.ID_PARSE_ERROR,ioe);
}
}
/**Index of the schema name in a jar name on a db classpath*/
public static final int DBCP_SCHEMA_NAME = 0;
/**Index of the sql jar name in a jar name on a db classpath*/
public static final int DBCP_SQL_JAR_NAME = 1;
/**
Scan a database classpath from the string provided. This returns
an array with one qualified name per entry on the classpath. The
constants above describe the content of the returned names. This
raises an an exception if the string does not contain a valid database
class path.
classpath := item[:item]*
item := id.id
In the syntax braces ([]) show grouping. '*' means repeat 0 or more times.
The syntax for id is defined in IdUtil.
Classpath returned is a two part name.
If the class path is empty then this returns an array
of zero length.
@exception StandardException Oops
*/
public static String[][] parseDbClassPath(String input)
throws StandardException
{
//As a special case we accept a zero length dbclasspath.
if (input.length() == 0)
return new String[0][];
Vector v = new Vector();
java.io.StringReader r = new java.io.StringReader(input);
//
while (true)
{
try {
String[] thisQName = IdUtil.parseMultiPartSQLIdentifier(r);
if (thisQName.length != 2)
throw StandardException.newException(SQLState.DB_CLASS_PATH_PARSE_ERROR,input);
v.addElement(thisQName);
int delim = r.read();
if (delim != ':')
{
if (delim!=-1)
throw StandardException.newException(SQLState.DB_CLASS_PATH_PARSE_ERROR,input);
break;
}
}
catch (StandardException se){
if (se.getMessageId().equals(SQLState.ID_PARSE_ERROR))
throw StandardException.newException(SQLState.DB_CLASS_PATH_PARSE_ERROR,
se,input);
else
throw se;
}
catch (IOException ioe){
throw StandardException.newException(SQLState.DB_CLASS_PATH_PARSE_ERROR,ioe,input);
}
}
String[][] result = new String[v.size()][];
v.copyInto(result);
return result;
}
/*
** Methods that operate on lists of identifiers.
*/
/**
Scan a list of comma separated SQL identifiers from the string provided.
This returns an array with containing the normalized forms of the identifiers.
This raises an an exception if
the string does not contain a valid list of names.
@exception StandardException Oops
*/
public static String[] parseIdList(String p)
throws StandardException
{
if (p==null) return null;
StringReader r = new StringReader(p);
String[] result = parseIdList(r, true);
verifyEmpty(r);
return result;
}
/**
Parse a list of comma separated SQL identifiers returning
them a as elements in an array.
@param normalize true means return ids in nomral form, false means
return them as they were entered.
@exception StandardException Oops
*/
private static String[] parseIdList(StringReader r, boolean normalize)
throws StandardException
{
Vector v = new Vector();
while (true)
{
int delim;
try {
String thisId = IdUtil.parseId(r,normalize);
v.addElement(thisId);
r.mark(0);
delim = r.read();
if (delim != ',')
{
if (delim!=-1) r.reset();
break;
}
}
catch (StandardException se){
if (se.getMessageId().equals(SQLState.ID_LIST_PARSE_ERROR))
throw StandardException.newException(SQLState.ID_LIST_PARSE_ERROR,se);
else
throw se;
}
catch (IOException ioe){
throw StandardException.newException(SQLState.ID_LIST_PARSE_ERROR,ioe);
}
}
if (v.size() == 0) return null;
String[] result = new String[v.size()];
v.copyInto(result);
return result;
}
/**
Return an IdList with all the ids that in l1 and l2
or null if not ids are on both lists.
@param l1 An array of ids in normal form
@param l2 An array of ids in nomral form
*/
public static String intersect(String[] l1, String[] l2)
{
if (l1 == null || l2 == null) return null;
HashSet h = new HashSet();
for(int ix=0;ix