/* license-start
*
* Copyright (C) 2008 - 2013 Crispico, <http://www.crispico.com/>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details, at <http://www.gnu.org/licenses/>.
*
* Contributors:
* Crispico - Initial API and implementation
*
* license-end
*/
package org.flowerplatform.common.ied;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.StringTokenizer;
/**
* @author Mariana Gheorghe
*/
public class InplaceEditorLabelParser {
protected IInplaceEditorProvider provider;
public InplaceEditorLabelParser(IInplaceEditorProvider provider) {
this.provider = provider;
}
/////////////////////////////////
// Label -> Model
/////////////////////////////////
public InplaceEditorLabelParseResult parseAttributeLabel(String text) {
String propertyName = null;
String propertyStringType = null;
String visibility = null;
String defaultValue = null;
if (text == null || text.trim().length() == 0) {
// the new text is null
throw new InplaceEditorException("_UI_Metamodel_Edit_Property_No_Label");
}
// remove \n and \t if there is any
text = clearString(text);
int pos = 0;
pos = text.indexOf(":");
if (pos >= 0 && text.indexOf(":", pos+1) > 0) {
// the new text shouldn't contain ":" character more than one
throw new InplaceEditorException("_UI_Metamodel_Semicolumn_No");
}
String[] splitedText = text.split(":");
propertyName = splitedText.length > 0 ? splitedText[0] : "";
if (propertyName == null || propertyName.length() == 0) {
// the property name is null
throw new InplaceEditorException("_UI_Metamodel_Edit_Name_Null");
}
// remove white spaces from the beginning and the end of the name
propertyName = propertyName.trim();
if (propertyName.length() >= 1 && provider.isVisibilityCharacter(propertyName.charAt(0))) {
visibility = propertyName.charAt(0)+"";
propertyName = propertyName.substring(1).trim();
}
if (propertyName.length() == 0) {
// the property name is missing
throw new InplaceEditorException("_UI_Metamodel_Edit_Name_Null");
}
if (splitedText.length == 1 || splitedText[1].trim().length() == 0) {
// the property type is null
throw new InplaceEditorException("_UI_Metamodel_Edit_Type_Null");
} else if (splitedText.length > 1)
propertyStringType = splitedText[1].trim();
if (propertyStringType != null) { // if there is a type
pos = propertyStringType.indexOf('=');
if (pos >= 0) { // there is a default value set
defaultValue = propertyStringType.substring(pos + 1).trim();
propertyStringType = propertyStringType.substring(0, pos).trim();
if (defaultValue.length() == 0) {
throw new InplaceEditorException("_UI_Metamodel_Edit_Default_Value_Missing");
}
if (propertyStringType.length() == 0) {
throw new InplaceEditorException("_UI_Metamodel_Edit_Type_Null");
}
}
if (propertyStringType.lastIndexOf('.') == propertyStringType.length() - 1) {
throw new InplaceEditorException("_UI_Metamodel_Edit_Type_Invalid");
}
}
return new InplaceEditorLabelParseResult()
.setName(propertyName)
.setType(propertyStringType)
.setVisibility(visibility)
.setDefaultValue(defaultValue);
}
public InplaceEditorLabelParseResult parseOperationLabel(String text) {
String operationName = null;
String operationReturnTypeValue = null;
LinkedHashMap<String, InplaceEditorLabelParseResult> operationParameters = new LinkedHashMap<String, InplaceEditorLabelParseResult>();
String aux = null;
char visibility = 0;
String paranthesisContent = null;
HashMap<String, String> addedTypes = new HashMap<String, String>();
if (text == null || text.length() == 0) {
// the new text is null
throw new InplaceEditorException("_UI_ClassEdit_Operation_No_Label");
}
// remove \n and \t if there is any
text = clearString(text);
// use checkBrackets() instead of checkContainingCharOnlyOnce('(', ')')
// because brackets can appear in parameter declarations
// in default value expressions as: f(p:Type = new Type(10));
if (!checkBrackets(text)) {
// the operation must have one open bracket and closed bracket
throw new InplaceEditorException("_UI_ClassEdit_Operation_Must_Contain_Brackets");
}
// split the text using the following separator: (,),;
StringTokenizer token = new StringTokenizer(text, "(");
if (token != null && token.hasMoreTokens())
operationName = token.nextToken();
if (operationName == null || text.charAt(0) == '(' || text.charAt(0) == ')') {
throw new InplaceEditorException("_UI_ClassEdit_Operation_Name_Missing");
}
operationName = operationName.trim();
if (provider.isVisibilityCharacter(operationName.charAt(0))) {
visibility = operationName.charAt(0);
operationName = operationName.substring(1).trim();
}
if (operationName.length() == 0) {
// only white spaces instead of the operation name
throw new InplaceEditorException("_UI_ClassEdit_Operation_Name_Missing");
}
if (withoutParams(text.substring(text.indexOf('('))) == true) {
// the operation has no parameters
paranthesisContent = null;
aux = text.substring(text.lastIndexOf(')') + 1); // should contain
// the return
// type
} else {
// paramters
int paranthesisContentEnd = text.lastIndexOf(')');
paranthesisContent = text.substring(text.indexOf('(') + 1, paranthesisContentEnd);
// aux should contain the return type
if (paranthesisContentEnd + 1 < text.length()) {
if (text.endsWith(";")) {
aux = text.substring(paranthesisContentEnd + 1, text.length() - 1);
} else {
aux = text.substring(paranthesisContentEnd + 1);
}
} else {
aux = null;
}
}
// parse the string which should contain the return value
if (aux == null || aux.trim().length() == 0) {
// no return type
operationReturnTypeValue = null;
} else {
int pos = -1;
pos = aux.indexOf(":");
if (pos < 0) {
// the ':' character is missing => syntax error
throw new InplaceEditorException("_UI_ClassEdit_Operation_Points_Are_Missing");
}
if (!checkContaingCharOnlyOnce(aux, ':'))
throw new InplaceEditorException("_UI_ClassEdit_Operation_Points");
aux = aux.substring(pos + 1, aux.length()).trim();
if (aux == null || aux.length() == 0) {
// the operation type is missing when expecting to be present
// (':' character is present)
throw new InplaceEditorException("_UI_ClassEdit_Operation_Type_Missing");
}
if (aux != null) { // if there is a string type value
if (aux.lastIndexOf('.') == aux.length() - 1)
throw new InplaceEditorException("_UI_ClassEdit_Operation_Type_Invalid");
operationReturnTypeValue = aux;
}
}
if (operationReturnTypeValue == null) {
throw new InplaceEditorException("_UI_ClassEdit_Operation_Type_Missing");
}
aux = "";
String parameterError = null;
// parse the string which should contain the parameters
if (paranthesisContent != null && paranthesisContent.length() > 0) {
for (int i = 0; i < paranthesisContent.length(); i++) {
if (paranthesisContent.charAt(i) == ',' && !insideOpenBrackets(paranthesisContent, i)) {
parameterError = addParameterEntry(operationParameters, aux, addedTypes);
if (parameterError != null)
throw new InplaceEditorException(parameterError);
aux = ""; // reset aux
} else if (i == paranthesisContent.length() - 1) {
aux += paranthesisContent.charAt(i);
parameterError = addParameterEntry(operationParameters, aux, addedTypes);
if (parameterError != null)
throw new InplaceEditorException(parameterError);
} else {
aux += paranthesisContent.charAt(i);
}
}
}
operationName += "(";
for (InplaceEditorLabelParseResult parameter : operationParameters.values()) {
operationName += parameter.getType() + ",";
}
if (operationName.endsWith(",")) {
operationName = operationName.substring(0, operationName.length() - 1);
}
operationName += ")";
return new InplaceEditorLabelParseResult()
.setName(operationName)
.setType(operationReturnTypeValue)
.setVisibility(String.valueOf(visibility))
.setParameters((Collection<InplaceEditorLabelParseResult>) operationParameters.values());
}
public InplaceEditorLabelParseResult parseParameterLabel(String text) {
return parseAttributeLabel(text);
}
/////////////////////////////////
// Model -> Label
/////////////////////////////////
public String createAttributeLabel(InplaceEditorLabelParseResult model, boolean forEditing) {
String label = String.format("%s%s : %s",
model.getVisibility() == null ? "" : model.getVisibility(),
model.getName(),
forEditing ? model.getType() : getSimpleName(model.getType()));
if (model.getDefaultValue() != null) {
label += String.format(" = %s", model.getDefaultValue());
}
return label;
}
public String createOperationLabel(InplaceEditorLabelParseResult model, boolean forEditing) {
String parameters = new String();
for (InplaceEditorLabelParseResult parameter : model.getParameters()) {
parameters += createParameterLabel(parameter, forEditing) + ", ";
}
if (parameters.length() > 0) {
parameters = parameters.substring(0, parameters.length() - 2);
}
String label = String.format("%s%s(%s) : %s",
model.getVisibility(),
model.getName(),
parameters,
forEditing ? model.getType() : getSimpleName(model.getType()));
return label;
}
public String createParameterLabel(InplaceEditorLabelParseResult model, boolean forEditing) {
return createAttributeLabel(model, forEditing);
}
/////////////////////////////////
// Utils
/////////////////////////////////
protected String clearString(String text) {
text.replaceAll("\n", "");
text.replaceAll("\t", "");
return text;
}
/**
* Checks if String <code>s</code> contains a valid set of
* open and close brackets
*
* @param s
* @return <code>true</code> only if the given String contains at least one pair of brackets
* correctly opened and closed, meaning one <code>'('</code> closed by <code>')'</code>
*/
protected boolean checkBrackets(String s) {
int bracketPairs = 0;
boolean found = false; // ensure there is at least one pair
for (int i = 0; i < s.length() ; i++) {
char ch = s.charAt(i);
if (ch == '(') {
found = true;
bracketPairs ++;
}
else if (ch == ')')
bracketPairs --;
// more closing brackets then opened
if (bracketPairs < 0) {
return false;
}
}
if (bracketPairs != 0 || !found) {
return false;
} else
return true;
}
protected boolean withoutParams(String s) {
for (int i = 1; i < s.length(); i++)
if (s.charAt(i) == ' ') {
} else if (s.charAt(i) == ')')
return true;
else
return false;
return true;
}
/**
* Searches <code>s</code> for the given character, <code>c</code>.
* Note that when the checked character is not <em>"</em> or <em>'</em> the algorithm will
* ignore the character's aparitions inside String Literals or Character
* Literals (between <em>"</em> and <em>'</em> characters).
*
* @param s =
* the string to search in
* @param c =
* the character to be searched for
* @return true if the character 'c' appears only once in the given string
* 's'
*/
// TODO:: rename in closing brackets
protected boolean checkContaingCharOnlyOnce(String s, char c) {
int pos = -1;
if (c != '"' && c != '\'') {
// this will mark the entry and the exit to and from a String literal
boolean insideStringLiteral = false;
// will mark the entry and exit from a char literal
boolean insideCharLiteral = false;
int index = 0;
char lastChar = 0;
char chars[] = s.toCharArray();
for (char ch : chars) {
if (ch == '"') {
if (lastChar != '\\')
insideStringLiteral = !insideStringLiteral;
} else if (ch == '\'') {
if (lastChar != '\\')
insideCharLiteral = !insideCharLiteral;
} else if (ch == c && !insideStringLiteral && !insideCharLiteral) {
// found the searched char and it's not inside a String Literal
if (pos >= 0) // found it again
return false;
else
pos = index; // found it for the first time
}
lastChar = ch;
index ++;
}
if (pos < 0)
return false; // char not found;
else
return true;
} else {
pos = s.indexOf(c);
if (pos < 0) {
return false;
}
if (s.indexOf(c, pos + 1) > 0) {
return false;
}
return true;
}
}
/**
* If the given index is inside open brackets it returns true. Otherwise, it
* returns false.
*/
protected boolean insideOpenBrackets(String s, int index) {
int countOpen = 0;
int countClosed = 0;
for (int i = 0; i < index; i++)
if (s.charAt(i) == '<')
countOpen++;
else if (s.charAt(i) == '>')
countClosed++;
if (countOpen != countClosed) // inside brackets
return true;
else
// outside brackets
return false;
}
/**
* The method receives a string representing a potential pair:
* parameterName:parameterValue=defaultValue. The method calls
* <code>ClassMetamodelPropertyItemProvider.parsePropertyOrParameter()</code>
* which parses the string. If the parsing doesn't throw an exception, the
* new parameter is added to the parameter link list.
*
* @param operationParameters
* = <code>LinkedHashMap</code> containing the parameter name as
* key and an Object[] array with parameter type and parameter
* default value
* @param text
* @return
* @flowerModelElementId _IrLtVdXXEd6axtupYmR2VA
*/
protected String addParameterEntry(LinkedHashMap<String, InplaceEditorLabelParseResult> operationParameters, String text, HashMap<String, String> addedTypes) {
InplaceEditorLabelParseResult result = parseParameterLabel(text);
String parameterType = null;
if (result.getType() != null) { // if there is a type value
parameterType = result.getType();
}
if (parameterType == null)
return "_UI_ClassEdit_Type_CouldNot_Be_Created";
// all the conditions are satisfied => add the new parameter and its
// value to the parameter's map
operationParameters.put(result.getName(), result);
return null;
}
protected String getSimpleName(String fullyQualifiedName) {
int index = fullyQualifiedName.lastIndexOf(".");
if (index >= 0) {
return fullyQualifiedName.substring(++index);
}
return fullyQualifiedName;
}
}