mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
193 lines
4.1 KiB
Java
193 lines
4.1 KiB
Java
package io.ebeaninternal.server.deploy;
|
|
|
|
import java.util.Set;
|
|
|
|
/**
|
|
* Converts logical property names to database columns.
|
|
*/
|
|
public abstract class DeployParser {
|
|
|
|
/**
|
|
* used to identify sql literal.
|
|
*/
|
|
protected static final char SINGLE_QUOTE = '\'';
|
|
|
|
/**
|
|
* used to identify query named parameters.
|
|
*/
|
|
protected static final char COLON = ':';
|
|
|
|
/**
|
|
* Used to determine when a column name terminates.
|
|
*/
|
|
protected static final char UNDERSCORE = '_';
|
|
|
|
/**
|
|
* Used to determine when a column name terminates.
|
|
*/
|
|
protected static final char PERIOD = '.';
|
|
|
|
protected static final char OPEN_BRACKET = '(';
|
|
|
|
protected boolean encrypted;
|
|
|
|
protected String source;
|
|
|
|
protected StringBuilder sb;
|
|
|
|
protected int sourceLength;
|
|
|
|
protected int pos;
|
|
|
|
protected String priorWord;
|
|
|
|
protected String word;
|
|
|
|
protected char wordTerminator;
|
|
|
|
private StringBuilder wordBuffer;
|
|
|
|
protected abstract String convertWord();
|
|
|
|
public abstract String getDeployWord(String expression);
|
|
|
|
/**
|
|
* Return the join includes.
|
|
*/
|
|
public abstract Set<String> getIncludes();
|
|
|
|
public void setEncrypted(boolean encrypted) {
|
|
this.encrypted = encrypted;
|
|
}
|
|
|
|
public String parse(String source) {
|
|
|
|
if (source == null) {
|
|
return null;
|
|
}
|
|
|
|
pos = -1;
|
|
this.source = source;
|
|
this.sourceLength = source.length();
|
|
this.sb = new StringBuilder(source.length() + 20);
|
|
|
|
while (nextWord()) {
|
|
if (skipWordConvert()) {
|
|
sb.append(word);
|
|
priorWord = word;
|
|
} else {
|
|
String deployWord = convertWord();
|
|
sb.append(deployWord);
|
|
priorWord = deployWord;
|
|
}
|
|
if (pos < sourceLength) {
|
|
if (wordTerminator != OPEN_BRACKET) {
|
|
sb.append(wordTerminator);
|
|
}
|
|
if (wordTerminator == SINGLE_QUOTE) {
|
|
readLiteral();
|
|
}
|
|
}
|
|
}
|
|
|
|
return sb.toString();
|
|
}
|
|
|
|
protected boolean skipWordConvert() {
|
|
return false;
|
|
}
|
|
|
|
private boolean nextWord() {
|
|
|
|
if (!findWordStart()) {
|
|
return false;
|
|
}
|
|
|
|
wordBuffer = new StringBuilder();
|
|
wordBuffer.append(source.charAt(pos));
|
|
while (++pos < sourceLength) {
|
|
char ch = source.charAt(pos);
|
|
if (isWordPart(ch)) {
|
|
wordBuffer.append(ch);
|
|
} else {
|
|
wordTerminator = ch;
|
|
break;
|
|
}
|
|
}
|
|
|
|
word = wordBuffer.toString();
|
|
|
|
return true;
|
|
}
|
|
|
|
private boolean findWordStart() {
|
|
while (++pos < sourceLength) {
|
|
char ch = source.charAt(pos);
|
|
if (ch == SINGLE_QUOTE) {
|
|
// read a literal value and just
|
|
// append to string builder
|
|
sb.append(ch);
|
|
readLiteral();
|
|
} else if (ch == COLON) {
|
|
// read a named parameter
|
|
// just append to string builder
|
|
sb.append(ch);
|
|
readNamedParameter();
|
|
} else if (isWordStart(ch)) {
|
|
// its the start of a word that could
|
|
// be translated
|
|
return true;
|
|
} else {
|
|
sb.append(ch);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Read the rest of a literal value. These do not get translated so are just
|
|
* read and appended to the string builder.
|
|
*/
|
|
private void readLiteral() {
|
|
while (++pos < sourceLength) {
|
|
char ch = source.charAt(pos);
|
|
sb.append(ch);
|
|
if (ch == SINGLE_QUOTE) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Read a named parameter. These are not translated. They will be replaced
|
|
* by positioned parameters later.
|
|
*/
|
|
private void readNamedParameter() {
|
|
while (++pos < sourceLength) {
|
|
char ch = source.charAt(pos);
|
|
sb.append(ch);
|
|
if (Character.isWhitespace(ch)) {
|
|
break;
|
|
} else if (ch == ',') {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* return true if the char is a letter, digit or underscore.
|
|
*/
|
|
private boolean isWordPart(char ch) {
|
|
if (ch == OPEN_BRACKET) {
|
|
// include in the 'word' such that "count(" formula doesn't clash with property "count"
|
|
wordBuffer.append(ch);
|
|
return false;
|
|
}
|
|
return Character.isLetterOrDigit(ch) || ch == UNDERSCORE || ch == PERIOD;
|
|
}
|
|
|
|
private boolean isWordStart(char ch) {
|
|
return Character.isLetter(ch) || ch == UNDERSCORE;
|
|
}
|
|
}
|