/*******************************************************************************
* Copyright 2009, 2010 Innovation Gate GmbH. All Rights Reserved.
*
* This file is part of the OpenWGA server platform.
*
* OpenWGA 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, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, a special exception is granted by the copyright holders
* of OpenWGA called "OpenWGA plugin exception". You should have received
* a copy of this exception along with OpenWGA in file COPYING.
* If not, see <http://www.openwga.com/gpl-plugin-exception>.
*
* OpenWGA 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.
*
* You should have received a copy of the GNU General Public License
* along with OpenWGA in file COPYING.
* If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
package de.innovationgate.wgpublisher.webtml.utils;
import java.util.StringTokenizer;
import de.innovationgate.utils.WGUtils;
import de.innovationgate.webgate.api.WGAPIException;
import de.innovationgate.webgate.api.WGArea;
import de.innovationgate.webgate.api.WGCSSJSModule;
import de.innovationgate.webgate.api.WGContent;
import de.innovationgate.webgate.api.WGContentKey;
import de.innovationgate.webgate.api.WGContentNavigator;
import de.innovationgate.webgate.api.WGContentType;
import de.innovationgate.webgate.api.WGCreationException;
import de.innovationgate.webgate.api.WGDatabase;
import de.innovationgate.webgate.api.WGDocument;
import de.innovationgate.webgate.api.WGException;
import de.innovationgate.webgate.api.WGFileContainer;
import de.innovationgate.webgate.api.WGLanguage;
import de.innovationgate.webgate.api.WGLanguageChooser;
import de.innovationgate.webgate.api.WGStructEntry;
import de.innovationgate.webgate.api.WGTMLModule;
import de.innovationgate.webgate.api.WGUnavailableException;
import de.innovationgate.wgpublisher.WGPDispatcher;
import de.innovationgate.wgpublisher.lang.SingleLanguageChooser;
import de.innovationgate.wgpublisher.lang.WebTMLLanguageChooser;
import de.innovationgate.wgpublisher.plugins.WGAPlugin;
import de.innovationgate.wgpublisher.webtml.Base;
import de.innovationgate.wgpublisher.webtml.BaseTagStatus;
import de.innovationgate.wgpublisher.webtml.Query;
public class TMLContextExpression {
public static final int EXPRESSIONTYPE_SIMPLE = 1;
public static final int EXPRESSIONTYPE_FUNCTION = 2;
public static final int EXPRESSIONTYPE_PATH = 3;
private int _type;
private String _function;
private String _expression;
private String _role;
private WGLanguageChooser _languageChooser;
private String _defaultLanguage;
private boolean _explicitLanguageChoice = false;
public TMLContextExpression(String expression, TMLContext originContext, String defaultLanguage) throws WGAPIException {
WGLanguageChooser chooser = new WebTMLLanguageChooser(originContext.db(), originContext);
// Determine expression type and elements
if (expression.indexOf("/") != -1 && !expression.startsWith("$")) {
_type = EXPRESSIONTYPE_PATH;
_function = null;
_expression = expression;
}
else if (expression.indexOf(":") != -1) {
_type = EXPRESSIONTYPE_FUNCTION;
_function = expression.substring(0, expression.indexOf(":")).trim().toLowerCase();
_expression = expression.substring(expression.indexOf(":") + 1).trim();
}
else {
_type = EXPRESSIONTYPE_SIMPLE;
_function = null;
_expression = expression.toLowerCase().trim();
}
// Determine relevant languages, based on optional language suffix
if (_expression.endsWith(">") && _type != EXPRESSIONTYPE_PATH) {
String languageSuffix = _expression.substring(_expression.indexOf("<") + 1, _expression.indexOf(">")).toLowerCase();
if (languageSuffix.equals("")) {
languageSuffix = defaultLanguage;
}
chooser = new SingleLanguageChooser(languageSuffix);
_explicitLanguageChoice = true;
_expression = _expression.substring(0, _expression.lastIndexOf("<")).trim();
}
_languageChooser = chooser;
}
/**
* @return Returns the expression.
*/
public String getExpression() {
return _expression;
}
/**
* @return Returns the function.
*/
public String getFunction() {
return _function;
}
public int getType() {
return _type;
}
public String getRole() {
return _role;
}
public void setRole(String role) {
_role = role;
}
public String toString() {
if (getType() == EXPRESSIONTYPE_FUNCTION) {
return getFunction() + ":" + getExpression();
}
else {
return getExpression();
}
}
public TMLContext processExpression(TMLContext context, boolean returnContextOnError) {
TMLContext errorReturnContext;
if (returnContextOnError) {
errorReturnContext = context;
}
else {
errorReturnContext = null;
}
context.setLastError(null);
try {
// Retrieve and build neccessary objects and flags
WGDocument mainDocument = context.getmaincontext().getdocument();
WGContent mainContent = null;
if (context.getmaincontext().getdocument() instanceof WGContent) {
mainContent = context.getmaincontext().content();
}
else {
mainContent = mainDocument.getDatabase().getDummyContent(null);
}
// Navigation role
String navRole = context.getrole();
if (getRole() != null) {
navRole = getRole();
}
// Build content navigator
WGContentNavigator navigator = new WGContentNavigator(navRole, _languageChooser);
navigator.setOnlyPublished(!context.isbrowserinterface());
// Process by expression type
if (getType() == EXPRESSIONTYPE_PATH) {
return processExpressionPath(context, errorReturnContext);
}
else if (getType() == EXPRESSIONTYPE_FUNCTION) {
return processExpressionFunction(context, errorReturnContext, mainContent, navigator);
}
else {
return processSimpleExpression(context, errorReturnContext, navigator);
}
}
catch (WGCreationException e) {
context.setLastError("Creation exception processing context expression (" + toString() + ") : " + e.getMessage());
return errorReturnContext;
}
catch (WGAPIException e) {
context.setLastError("Unable to change context to (" + toString() + ") bc. of exception: " + e.getClass().getName() + " message: " + e.getMessage());
return errorReturnContext;
}
}
TMLContext processExpressionPath(TMLContext context, TMLContext errorReturnContext) throws WGAPIException {
// Cutoff obsolete path qualifier
String path = getExpression();
if (path.startsWith("path:")) {
path = path.substring(5);
}
// Parse tokens
StringTokenizer tokens = new StringTokenizer(path, "/", false);
while (tokens.hasMoreTokens()) {
String token = tokens.nextToken();
// Special function: When the token starts with a "$"
// it is the last token of the expression and we add eventually
// remaining tokens to it (F00004CAE)
if (token.startsWith("$") && tokens.hasMoreTokens()) {
token += "/" + WGUtils.joinRemainingTokens(tokens, "/");
}
token.trim();
TMLContextExpression expr = new TMLContextExpression(token, context, context.db().getDefaultLanguage());
expr.setRole(getRole());
TMLContext startContext = context;
context = expr.processExpression(startContext, false);
if (context == null) {
if (errorReturnContext != null) {
errorReturnContext.setLastError(startContext.getlasterror());
}
return errorReturnContext;
}
// Store the role from this context expression part (function "role:" sets this) at the parent
// so it can be used for further steps
setRole(expr.getRole());
}
return context;
}
TMLContext processSimpleExpression(TMLContext context, TMLContext errorReturnContext, WGContentNavigator navigator) throws WGAPIException {
String contextExpression = getExpression();
WGContent content = context.content();
if (content == null) {
context.setLastError(
"Cannot process expression '" + getExpression() + "' on a non-content context: " + context.getdocument().getDocumentKey());
return errorReturnContext;
}
WGStructEntry struct = content.getStructEntry();
WGDatabase db = context.getdocument().getDatabase();
if (contextExpression.equals("parent")) {
if (content.isDummy()) {
context.setLastError("Cannot retrieve parent of dummy content");
return errorReturnContext;
}
if (struct.isRoot()) {
context.setLastError("Cannot get parent of root document");
return errorReturnContext;
}
else {
WGContent parentContent = navigator.getParentContent(content);
if (parentContent != null) {
return context.getTMLContextForDocument(parentContent);
}
else {
context.setLastError(
"Could not retrieve parent content: "
+ content.getTitle()
+ " ("
+ content.getContentKey().toString()
+ ")");
return errorReturnContext;
}
}
}
else if (contextExpression.equals("root")) {
WGContent rootContent = null;
if (!content.isDummy()) {
rootContent = navigator.getRootContent(content);
}
else {
rootContent = content.getDatabase().getFirstReleasedContent(_languageChooser, true);
}
if (rootContent != null) {
return context.getTMLContextForDocument(rootContent);
}
else {
context.setLastError("Could not retrieve root content");
return errorReturnContext;
}
}
else if (contextExpression.equals("main") || contextExpression.equals("currentdocument")) {
TMLContext mainContext = context.getmaincontext();
return new TMLContext(mainContext.getdocument(), context);
}
else if (contextExpression.startsWith("children")) {
if (content.isDummy()) {
context.setLastError("Cannot retrieve children of dummy content");
return errorReturnContext;
}
String idxString = contextExpression.substring(contextExpression.indexOf("[") + 1, contextExpression.indexOf("]"));
int idx;
try {
idx = Integer.parseInt(idxString);
}
catch (NumberFormatException exc) {
context.setLastError("Can't parse children index: " + contextExpression);
return errorReturnContext;
}
WGContent childContent = navigator.getChildContent(content, idx, WGContent.SEARCHORDER_ASCENDING);
if (childContent != null) {
return context.getTMLContextForDocument(childContent);
}
else {
context.setLastError("Cant retrieve child content idx " + idx);
return errorReturnContext;
}
}
else if (contextExpression.equals("selectedchild")) {
if (content.isDummy()) {
context.setLastError("Cannot retrieve children of dummy content");
return errorReturnContext;
}
WGContent mainContentParent = context.getmaincontext().content();
while (mainContentParent != null && !mainContentParent.isDummy() && !mainContentParent.getStructEntry().isRoot()) {
if (mainContentParent.getStructEntry().getParentEntry().equals(struct)) {
return context.getTMLContextForDocument(mainContentParent);
}
else {
mainContentParent = navigator.getParentContent(mainContentParent);
}
}
context.setLastError("Could not retrieve selected child");
return errorReturnContext;
}
else if (contextExpression.startsWith("siblings")) {
if (content.isDummy()) {
context.setLastError("Cannot retrieve siblings of dummy content");
return errorReturnContext;
}
String idxString =
contextExpression.substring(contextExpression.indexOf("[") + 1, contextExpression.indexOf("]")).trim();
boolean relative = (idxString.charAt(0) == '+' || idxString.charAt(0) == '-' ? true : false);
if (idxString.charAt(0) == '+') {
idxString = idxString.substring(1);
}
int idx = 0;
try {
idx = Integer.parseInt(idxString);
}
catch (NumberFormatException exc) {
context.setLastError("Can't parse siblings index: " + contextExpression);
return errorReturnContext;
}
WGContent sibling = navigator.getSiblingContent(content, idx, relative);
if (sibling != null) {
return context.getTMLContextForDocument(sibling);
}
else {
context.setLastError("Could not retrieve sibling " + idxString);
return errorReturnContext;
}
}
else if (contextExpression.equals("this")) {
if (!_explicitLanguageChoice) {
return context;
}
if (content.isDummy()) {
WGLanguage lang = _languageChooser.selectDatabaseLanguage(db);
if (lang != null) {
return context.getTMLContextForDocument(db.getDummyContent(lang.getName()));
}
else {
return errorReturnContext;
}
}
TMLContext thisContext = context.getTMLContextForDocument(navigator.getRelevantContent(content.getStructEntry()));
if (thisContext != null) {
return thisContext;
}
else {
context.setLastError(
"Unable to find content for struct entry " + content.getStructEntry().getStructKey());
;
return errorReturnContext;
}
}
else if (contextExpression.equals("portlet")) {
TMLPortlet portlet = context.getportlet();
if (portlet != null) {
return portlet.getcontext();
}
else {
return errorReturnContext;
}
}
else if (contextExpression.equals("createddoc")) {
TMLForm form = context.gettmlform();
if (form != null && form.getcreateddoc() != null) {
return context.context((WGContent) form.getcreateddoc());
}
else {
context.setLastError("Cannot change context to created document because there is none");
return errorReturnContext;
}
}
else {
context.setLastError("Could not interpret context expression:" + contextExpression);
return errorReturnContext;
}
}
TMLContext processExpressionFunction(TMLContext context, TMLContext errorReturnContext, WGContent mainContent, WGContentNavigator navigator)
throws WGAPIException {
String contextFunction = getFunction();
String contextExpression = getExpression();
WGDatabase db = context.getdocument().getDatabase();
boolean isBI = (context.getDesignContext().getTag() != null ? context.getDesignContext().getTag().isBrowserInterface() : false);
// Retrieve context by content key or unid
if (contextFunction.equals("docid") || contextFunction.equals("content")) {
WGContent content = null;
WGContentKey tmpKey;
try {
tmpKey = WGContentKey.parse(contextExpression, context.getdocument().getDatabase());
}
catch (WGAPIException e) {
context.setLastError("Error parsing contextExpression. Exception: " + e.getClass().getName() + " message: " + e.getMessage());
return errorReturnContext;
}
if (tmpKey != null) {
content = context.content().getDatabase().getContentByKey(tmpKey);
if (content != null) {
return context.getTMLContextForDocument(content);
}
}
content =
WGPDispatcher.getContentByAnyKey(
contextExpression,
context.content().getDatabase(),
_languageChooser,
context.isbrowserinterface());
if (content != null) {
return context.getTMLContextForDocument(navigator.chooseRelevantContent(content, mainContent));
}
else {
context.setLastError("docid could not be resolved: " + contextExpression);
return errorReturnContext;
}
}
else if (contextFunction.equals("name")) {
WGContent content = _languageChooser.selectContentForName(context.content().getDatabase(), contextExpression, context.isbrowserinterface());
if (content != null) {
return context.getTMLContextForDocument(navigator.chooseRelevantContent(content, mainContent));
}
context.setLastError("Could not retrieve content for name: " + contextExpression);
return errorReturnContext;
}
// Retrieve the context of another tag
else if (contextFunction.equals("tag")) {
if (context.getDesignContext().getTag() == null) {
context.setLastError("Cannot retrieve tag because this script does not run on a WebTML page");
return errorReturnContext;
}
BaseTagStatus refTag = context.getDesignContext().getTag().getTagStatusById(contextExpression);
if (refTag != null) {
TMLContext tagContext = refTag.tmlContext;
if (tagContext != null) {
return tagContext;
}
else {
context.setLastError("Context of this Tag could not be retrieved: " + contextExpression);
return errorReturnContext;
}
}
else {
context.setLastError("Tag could not be retrieved: " + contextExpression);
return errorReturnContext;
}
}
else if (contextFunction.equals("db") || contextFunction.equals("plugin")) {
if (contextFunction.equals("plugin")) {
WGAPlugin plugin = context.getwgacore().getPluginSet().getPluginByUniqueName(contextExpression);
if (plugin != null) {
contextExpression = plugin.buildDatabaseKey();
}
else {
context.setLastError("Unknown plugin unique name: " + contextExpression);
return errorReturnContext;
}
}
WGDatabase dbTarget = null;
try {
dbTarget = context.db(context.resolveDBKey(contextExpression));
}
catch (WGUnavailableException e1) {
context.setLastError("Database '" + contextExpression + "' is currently unavailable");
return errorReturnContext;
}
catch (WGException e) {
context.setLastError("Unable to open database '" + contextExpression + "'. Exception: " + e.getClass().getName() + " message: " + e.getMessage());
return errorReturnContext;
}
if (dbTarget == null) {
context.setLastError("No database with key " + contextExpression);
return errorReturnContext;
}
if (dbTarget.isSessionOpen() == false) {
context.setLastError("User cannot open database '" + contextExpression + "'");
return errorReturnContext;
}
if (dbTarget.getSessionContext().getAccessLevel() <= WGDatabase.ACCESSLEVEL_NOACCESS) {
context.setLastError("User has no access to database '" + contextExpression + "'");
return errorReturnContext;
}
TMLContext dbContext = context.dbContext(dbTarget);
if (dbContext == null) {
context.setLastError("Target database " + contextExpression + " does not support db context changes");
return errorReturnContext;
}
else {
return dbContext;
}
}
else if (contextFunction.equals("area")) {
WGArea area = db.getArea(contextExpression);
if (area == null) {
context.setLastError("No area of name '" + contextExpression + "'");
return errorReturnContext;
}
WGContent content = navigator.getRootContent(area);
if (content != null) {
return context.getTMLContextForDocument(content);
}
else {
context.setLastError("No root content in area '" + contextExpression + "'");
return errorReturnContext;
}
}
else if (contextFunction.equals("query")) {
if (context.getDesignContext().getTag() == null) {
context.setLastError("Cannot retrieve tag because this script does not run on a WebTML page");
return errorReturnContext;
}
BaseTagStatus tag = context.getDesignContext().getTag().getTagStatusById(contextExpression);
if (tag != null && tag instanceof Query.Status) {
Query.Status queryTag = (Query.Status) tag;
WGContent content = queryTag.getFirstContent();
if (content != null) {
return context.getTMLContextForDocument(queryTag.getFirstContent());
}
else {
context.setLastError(
"Could not retrieve context by query tag \"" + contextExpression + "\". Query had no result.");
return errorReturnContext;
}
}
else {
context.setLastError("No query tag with id: " + contextExpression);
return errorReturnContext;
}
}
else if (contextFunction.equals("role")) {
setRole(contextExpression);
return context;
}
else if (contextFunction.equals("relation")) {
WGContent relationContent = context.content().getRelation(contextExpression);
if (relationContent != null) {
return context.getTMLContextForDocument(relationContent);
}
else {
context.setLastError("Content " + context.meta("KEY") + " has no relation named '" + contextExpression + "'");
return errorReturnContext;
}
}
else if (contextFunction.equals("level")) {
int level;
try {
level = Integer.parseInt(contextExpression);
}
catch (NumberFormatException e) {
context.setLastError("Cannot be parsed as level number: " + contextExpression);
return errorReturnContext;
}
WGContent con = context.content();
if (!con.hasCompleteRelationships()) {
context.setLastError("Current content does not belong to a page hierarchy");
return errorReturnContext;
}
WGStructEntry struct = con.getStructEntry();
while (struct != null && struct.getLevel() > level) {
struct = struct.getParentEntry();
}
if (struct != null) {
WGContent targetCon = _languageChooser.selectContentForPage(struct, isBI);
if (targetCon != null) {
return context.getTMLContextForDocument(targetCon);
}
else {
context.setLastError("Page on level " + level + " has no appropriate content document");
return errorReturnContext;
}
}
else {
context.setLastError("Failed to go to page level " + level);
return errorReturnContext;
}
}
else if (contextFunction.equals("np")) {
String namePart = UniqueNamePartFormatter.INSTANCE.format(contextExpression);
String uname = context.content().getUniqueName();
if (WGUtils.isEmpty(uname)) {
uname = context.content().getStructEntry().getUniqueName();
}
String fullname;
if (!WGUtils.isEmpty(uname)) {
fullname = uname + "." + namePart;
}
else {
fullname = namePart;
}
WGContent content = _languageChooser.selectContentForName(context.content().getDatabase(), fullname, context.isbrowserinterface());
if (content != null) {
return context.getTMLContextForDocument(navigator.chooseRelevantContent(content, mainContent));
}
context.setLastError("Could not retrieve content for hdb unique name " + fullname);
return errorReturnContext;
}
// From here special functions for BI
else if (contextFunction.equals("$struct")) {
WGStructEntry entry = db.getStructEntryByKey(contextExpression);
if (entry != null) {
return context.getTMLContextForDocument(entry);
}
else {
context.setLastError("Could not retrieve struct entry with key '" + contextExpression + "'");
return errorReturnContext;
}
}
else if (contextFunction.equals("$area")) {
WGArea area = db.getArea(contextExpression);
if (area != null) {
return context.getTMLContextForDocument(area);
}
else {
context.setLastError("Could not retrieve area with name '" + contextExpression + "'");
return errorReturnContext;
}
}
else if (contextFunction.equals("$contenttype")) {
WGContentType contentType = db.getContentType(contextExpression);
if (contentType != null) {
return context.getTMLContextForDocument(contentType);
}
else {
context.setLastError("Could not retrieve contenttype with name '" + contextExpression + "'");
return errorReturnContext;
}
}
else if (contextFunction.equals("$language")) {
WGLanguage language = db.getLanguage(contextExpression);
if (language != null) {
return context.getTMLContextForDocument(language);
}
else {
context.setLastError("Could not retrieve language with name '" + contextExpression + "'");
return errorReturnContext;
}
}
else if (contextFunction.equals("$tml")) {
int commaPos = contextExpression.indexOf(",");
String name = contextExpression.substring(0, commaPos);
String mediaKey = contextExpression.substring(commaPos + 1);
WGTMLModule tml = db.getTMLModule(name, mediaKey);
if (tml != null) {
return context.getTMLContextForDocument(tml);
}
else {
context.setLastError("Could not retrieve tml with name '" + name + "' and media key '" + mediaKey + "'");
return errorReturnContext;
}
}
else if (contextFunction.equals("$cssjs")) {
String libName = contextExpression;
String type = null;
int commaPos = libName.indexOf(",");
if (commaPos != -1) {
type = libName.substring(commaPos+1).trim();
libName = libName.substring(commaPos).trim();
}
WGCSSJSModule lib;
if (type != null) {
lib = db.getCSSJSModule(libName, type);
}
else {
lib = db.getCSSJSModule(libName);
}
if (lib != null) {
return context.getTMLContextForDocument(lib);
}
else {
context.setLastError("Could not retrieve css/js module with name '" + contextExpression + "'");
return errorReturnContext;
}
}
else if (contextFunction.equals("$filecontainer")) {
WGFileContainer cont = db.getFileContainer(contextExpression);
if (cont != null) {
return context.getTMLContextForDocument(cont);
}
else {
context.setLastError("Could not retrieve file container with name '" + contextExpression + "'");
return errorReturnContext;
}
}
else {
context.setLastError("Context function could not be interpreted: " + contextFunction);
return errorReturnContext;
}
}
}