Java tutorial
/** * This file is part of PaxmlCore. * * PaxmlCore is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * PaxmlCore 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with PaxmlCore. If not, see <http://www.gnu.org/licenses/>. */ package org.paxml.core; import java.io.Closeable; import java.io.File; import java.io.Flushable; import java.io.IOException; import java.io.PrintStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Properties; import java.util.Set; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.jxpath.BasicVariables; import org.apache.commons.jxpath.ClassFunctions; import org.apache.commons.jxpath.FunctionLibrary; import org.apache.commons.jxpath.Functions; import org.apache.commons.jxpath.IdentityManager; import org.apache.commons.jxpath.JXPathContext; import org.apache.commons.jxpath.Pointer; import org.apache.commons.jxpath.Variables; import org.apache.commons.jxpath.ri.model.beans.BeanPointerFactory; import org.apache.commons.jxpath.ri.model.beans.NullPointer; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.paxml.annotation.Util; import org.paxml.el.IUtilFunctionsFactory; import org.paxml.file.IFile; import org.paxml.launch.Paxml; import org.paxml.security.Secret; import org.paxml.security.SecretRepository; import org.paxml.tag.ITag; import org.paxml.tag.ITagLibrary; import org.paxml.tag.invoker.FileInvokerTag; import org.paxml.util.CryptoUtils; import org.paxml.util.PaxmlUtils; import org.paxml.util.ReflectUtils; import org.paxml.util.XmlUtils; import org.springframework.core.io.Resource; /** * The execution context. * * @author Xuetao Niu * */ public class Context implements IdentityManager { private static final Log log = LogFactory.getLog(Context.class); /** * The scope definition of data. * * @author Xuetao Niu * */ public static enum Scope { /** * The local scope. */ LOCAL, /** * The parameter scope. */ PARAMETER } /** * The private context keys. * * @author Xuetao Niu * */ private static enum PrivateKeys { /** * The paxml listener key. */ PAXML_LISTENER, /** * The entity listener key. */ ENTITY_LISTENER, /** * The tag listener key. */ TAG_LISTENER, /** * The invocation result. */ RESULT, /** * Stack. */ STACK, /** * Paxml. */ PAXML, /** * Context where the exception was created. */ EXCEPTION_CONTEXT, /** * The locale. */ LOCALE, /** * The const overwritable flag. */ CONST_OVERWRITABLE, /** * The const names that are loaded as properties. */ PROPERTIES, /** * The key for Closeables. */ CLOSEABLES, /** * The key for all files */ FILES } /** * This is the representation of execution stack. * * @author Xuetao Niu * */ public static class Stack extends LinkedList<ITag> { /** * Interface for traversing stack. * * @author Xuetao Niu * */ public static interface IStackTraverser { /** * Called upon visiting each tag and entity * * @param entity * the entity * @param tag * the tag * @return true to continue traversal, false to stop traversal */ boolean onItem(IEntity entity, ITag tag); } private boolean exit = false; /** * Print out the stack. * * @param out * the target to print out * @throws IOException * thrown while accessing the stream */ public void print(PrintStream out) throws IOException { for (ITag tag : this) { out.println(tag.toString()); } } public boolean isExiting() { return exit; } /** * Mark global termination, so that no more tags will be executed. * */ public void exit() { this.exit = true; } /** * Visit each tag & entity combination of the stack. * * @param t * the event handler */ public void traverse(IStackTraverser t) { IEntity entity = null; for (ITag tag : this) { if (!(tag instanceof IEntity)) { if (entity != tag.getEntity() || tag instanceof FileInvokerTag) { entity = tag.getEntity(); if (!t.onItem(entity, tag)) { break; } } } } } } /** * The name of the default parameter. */ public static final String DEFAULT_VALUE_NAME = "value"; /** * The global xpath variable name. */ public static final String XPATH_NAME_GLOBAL_VAR = "global"; /** * The local xpath variable name. */ public static final String XPATH_NAME_LOCAL_VAR = "local"; private static final ThreadLocal<Context> THREAD_CONTEXT = new ThreadLocal<Context>(); private IEntity entity; private final long processId; private final long id; private boolean returning = false; private final ObjectTree idConstsMap = new ObjectTree(null); private final Map<String, String> idToTagName = new HashMap<String, String>(); private final Map<Object, Object> localMap = new LinkedHashMap<Object, Object>(0); private final Map<Object, Object> globalMap; private final Context parent; private final Context root; /** * Create from parent context. * * @param parent * the parent context, null is not allowed. * */ public Context(final Context parent) { if (parent == null) { throw new PaxmlRuntimeException("Parent context not given!"); } this.parent = parent; root = parent.root; this.processId = root.processId; this.id = parent.id + 1; globalMap = parent.globalMap; } /** * Create a root context. * * @param initialProperties * initial properties, can be null * @param processId * the virtual processId which is just a label. It depends on the * calling application to assign meaning. */ public Context(Properties initialProperties, long processId) { root = this; parent = null; this.processId = processId; this.id = 0; globalMap = new LinkedHashMap<Object, Object>(0); addProperties(initialProperties); } /** * Add properties to this level of context. * * @param properties * the properties where string keys will be taken as constants, * object keys will be global internal objects */ public void addProperties(Properties properties) { if (properties == null) { return; } for (Map.Entry<Object, Object> entry : properties.entrySet()) { Object key = entry.getKey(); if (key instanceof String) { addConst((String) key, (String) key, entry.getValue(), true); } else { setInternalObject(key, entry.getValue(), true); } } } /** * Get the virtual process id. A virtual process id is uniquely identfying * an execution of a out-most scenario. * * @return the virtual process id. */ public long getProcessId() { return processId; } /** * Get the context associated with the thread context. * * @return the context, null if not associated. */ public static Context getCurrentContext() { return THREAD_CONTEXT.get(); } /** * Set the current thread context to null. */ public static void cleanCurrentThreadContext() { THREAD_CONTEXT.set(null); } /** * Set this context as the current thread context. */ public void setAsCurrentThreadContext() { THREAD_CONTEXT.set(this); } /** * Add a data object with id and tag name for xpath usage. * * @param id * the id of the object * @param rootTagName * the root tag name for xpath usage. * @param c * the data object * @param checkConflict * true to prevent id conflicts, false to overwrite the existing * value with the same id. * */ public void addConst(String id, String rootTagName, Object c, boolean checkConflict) { if (checkConflict && idConstsMap.get(id) != null) { throw new PaxmlRuntimeException("Const with id '" + id + "' already exists!"); } idConstsMap.addValue(id, c); if (null != rootTagName) { idToTagName.put(id, rootTagName); } } /** * Set a data object in context, overwriting existing const by id. * * @param id * the id of the const to set and overwrite if exists. * @param rootTagName * the root tag name of the new const to set. * @param c * the new const * @param checkConflict * true to check id conflicts if there is conflict, exception * will be thrown, false not to. * @return the existing data, or null if there isn't existing data with the * id. */ public Object setConst(String id, String rootTagName, Object c, boolean checkConflict) { final Object existing = idConstsMap.put(id, c); if (existing != null && checkConflict) { throw new PaxmlRuntimeException("Id conflict: " + id); } if (null != rootTagName) { idToTagName.put(id, rootTagName); } return existing; } /** * Set a bunch of consts into the current context. * * @param map * the map containing the consts * @param rootTags * the root tags for the map parameter. * @param checkConflict * true to assert no ids conflicts before setting, false not to * assert. */ public void setConsts(Map<String, Object> map, Map<String, String> rootTags, boolean checkConflict) { if (checkConflict) { Collection<String> overlap = CollectionUtils.intersection(map.keySet(), idConstsMap.keySet()); if (overlap.size() > 0) { throw new PaxmlRuntimeException("The followng id conflicts detected: " + overlap); } } idConstsMap.putAll(map); if (rootTags != null) { idToTagName.putAll(rootTags); } } /** * Remove an added const. * * @param id * the id * @return the previously existing const, or null if no such value existed. */ public Object removeConst(String id) { idToTagName.remove(id); return idConstsMap.remove(id); } /** * Get the internal object with key. An internal object is searched always * from the root ancestor context. Internal object can be used as extension * in tag execution. It is highly recommended to use private enum constants * as keys. * * @param key * the key. * @param global * true to get from global scope, false to get from the current * context scope. * @return the value. null if not found. */ public Object getInternalObject(Object key, boolean global) { if (global) { return globalMap.get(key); } else { return localMap.get(key); } } /** * Set an internal object's value. * * @param key * the key * @param value * the value * @param global * true to set in global scope, false to set in the current * context scope. * @return the existing value, null if no existing value. */ public Object setInternalObject(Object key, Object value, boolean global) { if (global) { return globalMap.put(key, value); } else { return localMap.put(key, value); } } /** * Get the local object. * * @param key * the object key * @param searchParents * true to search parent, false not to * @return the object or null. */ public Object getLocalInternalObject(Object key, boolean searchParents) { Object value = getInternalObject(key, false); if (value == null && searchParents && parent != null) { value = parent.getLocalInternalObject(key, searchParents); } return value; } /** * Remove an internal object. * * @param key * the key * @param global * true to from from global scope, false to from from the current * context scope. * @return null if nothing exists with the key, otherwise the removed * object. */ public Object removeInternalObject(Object key, boolean global) { if (global) { return globalMap.remove(key); } else { return localMap.remove(key); } } /** * Get a data object with id. * * @param id * the id * @param searchParent * true to search in parent contexts, false not to. * @return null if not found, otherwise the data object. */ public Object getConst(String id, boolean searchParent) { if (idConstsMap.containsKey(id)) { return idConstsMap.get(id); } if (searchParent && parent != null) { return parent.getConst(id, true); } return null; } /** * * Find const id by given value. * * @param obj * the given value * @param strict * true to do pointer comparison, false to do object equality * comparison * @param searchParent * true to look also in parent contexts, false only look in the * current context * @param excludes * the ids to exclude * @return the id or null if no found. * */ public String findConstId(Object obj, boolean strict, boolean searchParent, String... excludes) { final Set<String> ex = new HashSet<String>(Arrays.asList(excludes)); if (strict) { for (Map.Entry<String, Object> entry : idConstsMap.entrySet()) { if (entry.getValue() == obj && !ex.contains(entry.getKey())) { return entry.getKey(); } } } else { for (Map.Entry<String, Object> entry : idConstsMap.entrySet()) { Object v = entry.getValue(); final boolean exclude = ex.contains(entry.getKey()); if (v == null && obj == null && !exclude) { return entry.getKey(); } if (v != null && v.equals(obj) && !exclude) { return entry.getKey(); } } } if (searchParent && parent != null) { return parent.findConstId(obj, strict, true, excludes); } return null; } /** * Get a data object with id and expected type. * * @param <T> * the expected type to convert to * @param id * the id * @param searchParent * true to search in parent contexts, false not to. * @param clazz * the expected class * @return null if not found, otherwise the data in expected class */ public <T> T getConst(String id, boolean searchParent, Class<? extends T> clazz) { Object c = getConst(id, searchParent); return (T) ReflectUtils.coerceType(c, clazz); } /** * Check the existence of an data object. * * @param id * the id * @param searchParent * true to search in parent contexts, false not to. * @return true if exists, false if not */ public boolean hasConstId(String id, boolean searchParent) { if (idConstsMap.containsKey(id)) { return true; } if (searchParent && parent != null) { return parent.hasConstId(id, searchParent); } return false; } /** * Get a data object list with an id. * * @param id * the id * @param searchParent * true to search in parent contexts, false not to. * @return null if no such data object exists with given id, otherwise, * always a list of the data object. If the data object itself is * not a list, then the object will be put into a new list to * return. Nothing in the context will be changed. */ public List<?> getConstWithIdAsList(String id, boolean searchParent) { Object obj = getConst(id, searchParent); if (obj == null) { return null; } if (obj instanceof List) { return (List<?>) obj; } List<Object> list = new ArrayList<Object>(1); list.add(obj); return list; } public Context getRootContext() { return root; } /** * Get the paxml process execution listeners list. * * @param autoCreate * true to create one if not existing yet, false to return null * if not existing yet. * @return the list. */ public List<IExecutionListener> getPaxmlExecutionListeners(boolean autoCreate) { List<IExecutionListener> list = (List<IExecutionListener>) getInternalObject(PrivateKeys.PAXML_LISTENER, true); if (list == null && autoCreate) { list = new ArrayList<IExecutionListener>(); setPaxmlExecutionListeners(list); } return list; } /** * Set paxml process execution listeners list. * * @param paxmlExecutionListeners * the list, null to remove the list. * @return the existing list. */ public List<IExecutionListener> setPaxmlExecutionListeners(List<IExecutionListener> paxmlExecutionListeners) { if (paxmlExecutionListeners == null) { return (List<IExecutionListener>) removeInternalObject(PrivateKeys.PAXML_LISTENER, true); } else { return (List<IExecutionListener>) setInternalObject(PrivateKeys.PAXML_LISTENER, paxmlExecutionListeners, true); } } /** * Get the entity execution listeners list. * * @param autoCreate * true to create one if not existing yet, false to return null * if not existing yet. * @return the list. */ public List<IEntityExecutionListener> getEntityExecutionListeners(boolean autoCreate) { List<IEntityExecutionListener> list = (List<IEntityExecutionListener>) getInternalObject( PrivateKeys.ENTITY_LISTENER, true); if (list == null && autoCreate) { list = new ArrayList<IEntityExecutionListener>(); setEntityExecutionListeners(list); } return list; } /** * Set paxml entity execution listeners list. * * @param entityExecutionListeners * the list, null to remove the list. * @return the existing list. */ public List<IEntityExecutionListener> setEntityExecutionListeners( List<IEntityExecutionListener> entityExecutionListeners) { if (entityExecutionListeners == null) { return (List<IEntityExecutionListener>) removeInternalObject(PrivateKeys.ENTITY_LISTENER, true); } else { return (List<IEntityExecutionListener>) setInternalObject(PrivateKeys.ENTITY_LISTENER, entityExecutionListeners, true); } } /** * Get the tag execution listeners list. * * @param autoCreate * true to create one if not existing yet, false to return null * if not existing yet. * @return the list. */ public List<ITagExecutionListener> getTagExecutionListeners(boolean autoCreate) { List<ITagExecutionListener> list = (List<ITagExecutionListener>) getInternalObject(PrivateKeys.TAG_LISTENER, true); if (list == null && autoCreate) { list = new ArrayList<ITagExecutionListener>(); setTagExecutionListeners(list); } return list; } /** * Set paxml tag execution listeners list. * * @param tagExecutionListeners * the list, null to remove the list. * @return the existing list. */ public List<ITagExecutionListener> setTagExecutionListeners(List<ITagExecutionListener> tagExecutionListeners) { if (tagExecutionListeners == null) { return (List<ITagExecutionListener>) removeInternalObject(PrivateKeys.TAG_LISTENER, true); } else { return (List<ITagExecutionListener>) setInternalObject(PrivateKeys.TAG_LISTENER, tagExecutionListeners, true); } } /** * Get the ids of the consts. * * @return a read only id set, never null */ public Set<String> getConstIds() { return Collections.unmodifiableSet(idConstsMap.keySet()); } /** * Get a copy of the map keyed with tag names. * * @param mergeParents * true to merge with the map from parent context, false not to. * @param includesRoot * true to include the map from the root context, false not to. * If the current is already root context, set to false will * result in empty map being returned. * @return the map copy. */ public ObjectTree getNameMap(boolean mergeParents, boolean includesRoot) { ObjectTree tree = new ObjectTree(null); if (includesRoot || !isRoot()) { for (Map.Entry<String, Object> entry : idConstsMap.entrySet()) { Object value = entry.getValue(); String id = entry.getKey(); String tagName = idToTagName.get(id); if (tagName != null) { tree.addValue(tagName, value); } } } if (mergeParents && parent != null) { tree.addValues(parent.getNameMap(mergeParents, includesRoot)); } return tree; } /** * Get a copy of the map keyed with ids. * * @param mergeParents * true to merge with the map from parent context, false not to. * @param includesRoot * true to include the map from the root context, false not to. * If the current is already root context, set to false will * result in empty map being returned. * @return the map copy. */ public Map<String, Object> getIdMap(boolean mergeParents, boolean includesRoot) { if (mergeParents) { Map<String, Object> map = new HashMap<String, Object>(); if (parent != null && (includesRoot || parent != root)) { map.putAll(parent.getIdMap(mergeParents, includesRoot)); } if (includesRoot || !isRoot()) { map.putAll(idConstsMap); } return map; } else { if (includesRoot || !isRoot()) { return new LinkedHashMap<String, Object>(idConstsMap); } else { return new LinkedHashMap<String, Object>(0); } } } /** * Check if the current context is a root context which has no parent. * * @return true yes, false no. */ public boolean isRoot() { return this == root; } /** * Select objects with xpath. * * @param from * the object to select properties from, null to select from * entire context. * @param xpath * the xpath * @return either a list of objects that satisfies the xpath, or the object * itself if the xpath results in one object to be selected. */ public Object xpathSelect(Object from, String xpath) { return xpathSelect(from, xpath, false); } /** * Select objects with xpath. * * @param xpath * the xpath * @return either a list of objects that satisfies the xpath, or the object * itself if the xpath results in one object to be selected. */ public Object xpathSelect(String xpath) { return xpathSelect(null, xpath); } /** * Select objects with xpath. * * * @param xpath * the xpath * @param alwaysList * true to return a list with one item inside if the xpath * results in one object to be selected. * @return either a list of objects that satisfies the xpath, or the object * itself if the xpath results in one object to be selected and the * "alwaysList" parameter is false. */ public Object xpathSelect(String xpath, boolean alwaysList) { return xpathSelect(null, xpath, alwaysList); } /** * Select objects with xpath. * * @param from * the object to select properties from, null to select from * entire context. * @param xpath * the xpath * @param alwaysList * true to return a list with one item inside if the xpath * results in one object to be selected. * @return either a list of objects that satisfies the xpath, or the object * itself if the xpath results in one object to be selected and the * "alwaysList" parameter is false. */ public Object xpathSelect(Object from, String xpath, boolean alwaysList) { Variables vars = new BasicVariables(); if (from == null) { ObjectTree nameGlobal = getRootContext().getNameMap(false, true); ObjectTree nameLocal = getNameMap(true, false); vars.declareVariable(XPATH_NAME_GLOBAL_VAR, nameGlobal); vars.declareVariable(XPATH_NAME_LOCAL_VAR, nameLocal); ObjectTree nameAuto = new ObjectTree(null, nameGlobal); nameAuto.addValues(nameLocal); from = nameAuto; } JXPathContext xpathContext = JXPathContext.newContext(from); xpathContext.setVariables(vars); xpathContext.setIdentityManager(this); setXpathFunctions(xpathContext); try { Object selected = xpathContext.iterate(xpath); List<Object> list = new ArrayList<Object>(1); if (selected instanceof Iterator) { final Iterator<?> it = (Iterator<?>) selected; while (it.hasNext()) { Object obj = it.next(); list.add(getXpathResultObject(obj)); } if (list.size() == 1) { selected = list.get(0); } else { selected = list; } } else { selected = getXpathResultObject(selected); if (selected != null && alwaysList) { list.add(selected); } } if (alwaysList) { return list; } else { if (selected instanceof List) { list = (List) selected; final int size = list.size(); if (size == 0) { return null; } else if (size == 1) { return list.get(0); } } return selected; } } catch (NullPointerException e) { // when jxpath throws null pointer exception, it has problem // searching non-existing paths return null; } } /** * {@inheritDoc}. Find a const's id. */ @Override public Pointer getPointerByID(JXPathContext xpc, String id) { Object value = getConst(id, true); if (value == null) { return new NullPointer(null, id); } else { return new BeanPointerFactory().createNodePointer(null, value, null); // return new BeanPointer(null, value, // JXPathIntrospector.getBeanInfo(value.getClass()), null); } } private void setXpathFunctions(JXPathContext xpathContext) { Functions existing = xpathContext.getFunctions(); final FunctionLibrary funcLib; if (existing == null) { funcLib = new FunctionLibrary(); } else if (existing instanceof FunctionLibrary) { funcLib = (FunctionLibrary) existing; } else { funcLib = new FunctionLibrary(); funcLib.addFunctions(existing); } for (ITagLibrary lib : getPaxml().getParser().getTagLibraries()) { for (String name : lib.getUtilFunctionsFactoryNames()) { Class<? extends IUtilFunctionsFactory> clazz = lib.getUtilFunctionsFactory(name); Class<?> xpathFunClass = ReflectUtils.createObject(clazz).getXpathUtilFunctions(this); if (xpathFunClass == null) { // skip this one continue; } Util util = ReflectUtils.getAnnotation(clazz, Util.class); if (util == null) { throw new PaxmlRuntimeException( "Internal error: util function factory is not annotated: " + clazz.getName()); } funcLib.addFunctions(new ClassFunctions(xpathFunClass, util.value())); } } xpathContext.setFunctions(funcLib); } private Object getXpathResultObject(Object obj) { if (obj instanceof Pointer) { obj = ((Pointer) obj).getValue(); } return obj; } /** * Push an tag into execution stack. * * @param tag * the tag */ public void pushStack(ITag tag) { getStack().push(tag); } /** * Pop a tag from execution stack. * */ public void popStack() { getStack().pop(); } /** * Get the current execution stack from context. * * @return the current stack from the execution context, never null */ public Stack getStack() { Stack stack = (Stack) getInternalObject(PrivateKeys.STACK, true); if (stack == null) { stack = new Stack(); setInternalObject(PrivateKeys.STACK, stack, true); } return stack; } /** * Set the invocation result. * * @param obj * the result */ public void setInvocationResult(Object obj) { setInternalObject(PrivateKeys.RESULT, obj, true); } /** * Get the current invocation result. * * @return the current result */ public Object getInvocationResult() { return getInternalObject(PrivateKeys.RESULT, true); } /** * Get the associated paxml object from global context. * * @return the paxml, or null if not found. */ public Paxml getPaxml() { return (Paxml) getInternalObject(PrivateKeys.PAXML, true); } /** * Set the associated paxml object in global context. * * @param paxml * the paxml */ public void setPaxml(Paxml paxml) { setInternalObject(PrivateKeys.PAXML, paxml, true); } /** * Set the context which originally throws the exception. * * @param context * the context */ public void setExceptionContext(Context context) { setInternalObject(PrivateKeys.EXCEPTION_CONTEXT, context, true); } /** * Get the context where the exception was originally created. * * @return the context, or null if no exceptions */ public Context getExceptionContext() { return (Context) getInternalObject(PrivateKeys.EXCEPTION_CONTEXT, true); } /** * Get the locale associated with the context and its parents. * * @return the associated locale, null if not associated. */ public Locale getLocale() { Locale loc = (Locale) getInternalObject(PrivateKeys.LOCALE, true); return loc; } /** * Set the locale associated with the context and its parents. * * @param locale * the locale */ public void setLocale(Locale locale) { setInternalObject(PrivateKeys.LOCALE, locale, true); } /** * Get all consts in a map. * * @return a read only map */ public Map<String, Object> getConsts() { return Collections.unmodifiableMap(idConstsMap); } /** * Get the mapping from const ids to root tag names. * * @return a read only map, where keys are const ids, values are * corresponding root tag names. */ public Map<String, String> getConstIdToRootNameMapping() { return Collections.unmodifiableMap(idToTagName); } /** * Get the internal map for holding the consts. NB! be careful when * modifying it. * * @return the map */ public ObjectTree getIdConstsMap() { return idConstsMap; } /** * Get the paxml entity for which the context is created. * * @return the paxml entity or null if it is root context */ public IEntity getEntity() { return entity; } /** * Set the paxml entity that the context is created for. * * @param entity * the paxml entity */ public void setEntity(IEntity entity) { this.entity = entity; } /** * Find the context created for the paxml entity. The search order is from * the current context up till the root context. * * @param e * the paxml entity * @return the context created for the given entity */ public Context findContextForEntity(final IEntity e) { Context context = this; do { if (e == context.entity) { return context; } context = context.parent; } while (context != null); return null; } /** * Find the caller's entity. * * @return the caller file's entity, null if has no caller. */ public IEntity findCallerEntity() { final IEntity e = getCurrentTag().getEntity(); Context context = findContextForEntity(e); if (context == null) { return null; } context = context.parent; while (!context.isRoot()) { if (null != context.entity) { return context.entity; } context = context.parent; } return null; } /** * Find the caller entity's context. * * @return the caller entity's context, null if has no caller. */ public Context findCallerContext() { final IEntity e = getCurrentTag().getEntity(); Context context = findContextForEntity(e); if (context == null) { return null; } context = context.parent; while (!context.isRoot()) { if (null != context.entity) { return context; } context = context.parent; } return null; } /** * Get the current tag being executed. * * @return the current tag, never null. */ public ITag getCurrentTag() { Stack stack = getStack(); if (stack.isEmpty()) { throw new PaxmlRuntimeException("No current tag!"); } return stack.getFirst(); } /** * Get the current entity being executed. * * @return the current entity, never null */ public IEntity getCurrentEntity() { return getCurrentTag().getEntity(); } /** * Get the current entity's context. * * @return the context, never null */ public Context getCurrentEntityContext() { return findContextForEntity(getCurrentEntity()); } /** * Get the default parameter named "value". * * @return the default param or null if not given. */ public Object getDefaultParameter() { return getConst(DEFAULT_VALUE_NAME, false); } /** * Check if it a const is overwritable in the current context. * * @return true if overwritable, false not. */ public boolean isConstOverwritable() { Boolean yes = (Boolean) getLocalInternalObject(PrivateKeys.CONST_OVERWRITABLE, false); return yes != null && yes; } /** * Set in the current context for a const being overwritable. * * @param yes * true overwritable, false not */ public void setConstOverwritable(boolean yes) { setInternalObject(PrivateKeys.CONST_OVERWRITABLE, yes, false); } /** * Get the const ids that are loaded as property. * * @param searchParent * true to search parent context, false not to * @return the set of ids, never null. */ public Set<String> getPropertyConstIds(boolean searchParent) { Set<String> set = getLocalPropertyConstIds(); if (searchParent && !isRoot()) { set.addAll(parent.getPropertyConstIds(searchParent)); } return set; } /** * Add a const id as property const. * * @param id * the id */ public void addPropertyConstId(String id) { getLocalPropertyConstIds().add(id); } private Set<String> getLocalPropertyConstIds() { Set<String> set = (Set<String>) getLocalInternalObject(PrivateKeys.PROPERTIES, false); if (set == null) { set = new LinkedHashSet<String>(); setInternalObject(PrivateKeys.PROPERTIES, set, false); } return set; } /** * Get the parent context. * * @return the parent */ public Context getParent() { return this.parent; } public boolean isReturning() { return returning; } public void setReturning(boolean returning) { this.returning = returning; } public void registerCloseable(Closeable... Closeables) { List<Closeable> list = (List) getInternalObject(PrivateKeys.CLOSEABLES, true); if (list == null) { list = new ArrayList<Closeable>(); setInternalObject(PrivateKeys.CLOSEABLES, list, true); } for (Closeable c : Closeables) { list.add(c); } } public void closeAllCloseables() { List<Closeable> list = (List) getInternalObject(PrivateKeys.CLOSEABLES, true); if (list != null) { for (Iterator<Closeable> it = list.iterator(); it.hasNext();) { Closeable c = it.next(); try { if (c instanceof Flushable) { ((Flushable) c).flush(); } } catch (IOException e) { // do nothing because it could be already closed. } finally { try { c.close(); it.remove(); } catch (Exception e) { // say nothing about it because it could be already // closed // by anything else } } } } } /** * Get the only file in the context. * * @return the file or null if no file at all or more than 1 file is in the * context */ public IFile getOnlyFile() { Map<String, IFile> map = getFiles(); if (map.size() == 1) { return map.values().iterator().next(); } return null; } private Map<String, IFile> getFiles() { Map<String, IFile> map = (Map<String, IFile>) getInternalObject(PrivateKeys.FILES, true); if (map == null) { map = new LinkedHashMap<String, IFile>(); setInternalObject(PrivateKeys.FILES, map, true); } return map; } /** * * @param file * @return */ public IFile getFile(Resource res) { String key = PaxmlUtils.getResourceFile(res); return getFiles().get(key); } public IFile getFile(File file) { return getFile(new FileSystemResource(file).getSpringResource()); } public IFile addFile(Resource res, IFile file) { String key = PaxmlUtils.getResourceFile(res); return getFiles().put(key, file); } public long getId() { return id; } public static Secret getSecret(String name) { String pwd = SecretRepository.getCurrentUserMasterKey(); if (pwd == null) { throw new PaxmlRuntimeException("No secret store password given!"); } String clear = CryptoUtils.getKey(null, pwd, name, null); if (clear == null) { return null; } return new Secret(name, clear); } public static Secret setSecret(String name, String value) { String pwd = SecretRepository.getCurrentUserMasterKey(); if (pwd == null) { throw new PaxmlRuntimeException("No secret store password given!"); } CryptoUtils.setKey(null, pwd, name, null, value); return new Secret(name, value); } public static void deleteSecret(String name) { String pwd = SecretRepository.getCurrentUserMasterKey(); if (pwd == null) { throw new PaxmlRuntimeException("No secret store password given!"); } CryptoUtils.deleteKey(null, pwd, name); } public String dump() { return XmlUtils.toXml(this, "context", null); } }