/* * Copyright 2001-2008 Geert Bevin <gbevin[remove] at uwyn dot com> * Licensed under the Apache License, Version 2.0 (the "License") * $Id */ package com.uwyn.rife.engine; import com.uwyn.rife.config.RifeConfig; import com.uwyn.rife.engine.elements.Redirect; import com.uwyn.rife.engine.exceptions.*; import com.uwyn.rife.ioc.HierarchicalProperties; import com.uwyn.rife.ioc.PropertyValue; import com.uwyn.rife.ioc.PropertyValueObject; import com.uwyn.rife.ioc.exceptions.PropertyValueException; import com.uwyn.rife.rep.Rep; import com.uwyn.rife.resources.ResourceFinder; import com.uwyn.rife.resources.ResourceFinderClasspath; import com.uwyn.rife.site.Constrained; import com.uwyn.rife.site.ConstrainedUtils; import com.uwyn.rife.site.ValidatedConstrained; import com.uwyn.rife.site.ValidationGroup; import com.uwyn.rife.tools.BeanUtils; import com.uwyn.rife.tools.FileUtils; import com.uwyn.rife.tools.StringUtils; import com.uwyn.rife.tools.exceptions.BeanUtilsException; import java.util.*; public class SiteBuilder { private ResourceFinder mResourceFinder = null; private SiteBuilder mParent = null; private SubsiteDeclaration mSubsiteDeclaration = null; private boolean mProcessed = false; private String mIdentifier = null; private String mDeclarationName = null; private String mSiteId = null; private String mUrlPrefix = null; private Stack<StateStore> mStateStores = null; private HashSet<String> mSubsiteHistory = null; private Site mSite = null; private Stack<GroupDeclaration> mGroupDeclarationsStack = null; private ArrayList<GroupDeclaration> mGroupDeclarations = null; private String mFallbackElementId = null; private String mArrivalElementId = null; private boolean mArrivalRedirect = false; private ElementDeclaration mArrivalElementDeclaration = null; private ArrayList<String> mDepartureIds = null; private LinkedHashMap<String, ElementDeclaration> mElementIdMapping = null; private ArrayList<ElementDeclaration> mElementDeclarations = null; private LinkedHashMap<String, SubsiteDeclaration> mChildSubsiteIdMapping = null; private ArrayList<SubsiteDeclaration> mChildSubsiteDeclarations = null; private LinkedHashMap<String, HashSet<DataLinkDeclaration>> mDataLinkMapping = null; private LinkedHashMap<String, HashSet<FlowLinkDeclaration>> mFlowLinkMapping = null; private LinkedHashMap<String, HashSet<AutoLinkDeclaration>> mAutoLinkMapping = null; private HierarchicalProperties mProperties = null; public SiteBuilder(String declarationName) { this(declarationName, ResourceFinderClasspath.getInstance()); } public SiteBuilder(String declarationName, ResourceFinder resourceFinder) { if (null == declarationName) throw new IllegalArgumentException("declarationName can't be null."); if (null == resourceFinder) throw new IllegalArgumentException("resourceFinder can't be null."); mResourceFinder = resourceFinder; initialize(declarationName); } private SiteBuilder(String declarationName, ResourceFinder resourceFinder, SiteBuilder parent, SubsiteDeclaration subsite) { assert declarationName != null; assert resourceFinder != null; assert parent != null; assert parent != this; assert subsite != null; mResourceFinder = resourceFinder; mParent = parent; mSubsiteDeclaration = subsite; initialize(declarationName); } private SiteBuilder initialize(String declarationName) { mDeclarationName = declarationName; if (null == getParent()) { mSite = new Site(); mSite.setDeclarationName(mDeclarationName); // if the site structure must be automatically reloaded // ensure that the same resource finder is used as the one // that found the element and site xml files if (RifeConfig.Engine.getSiteAutoReload()) { mSite.setResourceFinder(mResourceFinder); } mStateStores = new Stack<StateStore>(); mStateStores.push(StateStoreFactory.getInstance(StateStoreQuery.IDENTIFIER)); mGroupDeclarationsStack = new Stack<GroupDeclaration>(); mGroupDeclarations = new ArrayList<GroupDeclaration>(); mSubsiteHistory = new HashSet<String>(); addGroupDeclaration(new GroupDeclaration(this, declarationName)); } else { mSite = mParent.mSite; mStateStores = mSubsiteDeclaration.getStateStores(); mSubsiteHistory = new HashSet<String>(getParent().mSubsiteHistory); mGroupDeclarationsStack = new Stack<GroupDeclaration>(); mGroupDeclarations = new ArrayList<GroupDeclaration>(); addGroupDeclaration(mSubsiteDeclaration.getGroupDeclaration()); } mFallbackElementId = null; mArrivalElementId = null; mArrivalElementDeclaration = null; mDepartureIds = new ArrayList<String>(); mElementIdMapping = new LinkedHashMap<String, ElementDeclaration>(); mElementDeclarations = new ArrayList<ElementDeclaration>(); mChildSubsiteIdMapping = new LinkedHashMap<String, SubsiteDeclaration>(); mChildSubsiteDeclarations = new ArrayList<SubsiteDeclaration>(); mDataLinkMapping = new LinkedHashMap<String, HashSet<DataLinkDeclaration>>(); mFlowLinkMapping = new LinkedHashMap<String, HashSet<FlowLinkDeclaration>>(); mAutoLinkMapping = new LinkedHashMap<String, HashSet<AutoLinkDeclaration>>(); mProperties = new HierarchicalProperties(); // if this is the root site, the parent properties are those // of the repository if (null == mParent) { mProperties.setParent(Rep.getProperties()); } else { // the root site hasn't got a subsite declaration, so link // to its properties directly if (null == mParent.getSubsiteDeclaration()) { mProperties.setParent(mParent.getProperties()); } // for the real subsite, the parent should be the declaration properties // since they have precedence over the subsite properties themselves else { mProperties.setParent(mParent.getSubsiteDeclaration().getProperties()); } } return this; } public synchronized Site getSite() { SiteBuilder root = getRoot(); if (!mProcessed) { root.process(); } return root.mSite; } public String getSiteProcessorIdentifier() { return mIdentifier; } private synchronized void process() throws EngineException { String declaration = mDeclarationName; SiteProcessorFactory processor_factory = null; String identifier = SiteProcessorFactory.MANUAL_IDENTIFIER; int identifier_index = mDeclarationName.indexOf(":"); int extension_index = mDeclarationName.lastIndexOf("."); if (identifier_index != -1) { identifier = mDeclarationName.substring(0, identifier_index); declaration = mDeclarationName.substring(identifier_index+1); processor_factory = SiteProcessorFactory.getSiteProcessorFactory(identifier); } else if (extension_index != -1) { String extension = mDeclarationName.substring(extension_index+1); Collection<SiteProcessorFactory> factories = SiteProcessorFactory.getSiteProcessorFactories(); for (SiteProcessorFactory factory : factories) { if (factory.getExtension() != null && factory.getExtension().equals(extension)) { processor_factory = factory; break; } } if (null == processor_factory) { throw new SiteProcessorExtensionUnsupportedException(mDeclarationName, extension); } } else { processor_factory = SiteProcessorFactory.getSiteProcessorFactory(identifier); } if (null == processor_factory) { throw new SiteProcessorIdentifierUnsupportedException(mDeclarationName, identifier); } // check for circular subsites if (!identifier.equals(SiteProcessorFactory.MANUAL_IDENTIFIER)) { if (mSubsiteHistory.contains(mDeclarationName)) { throw new CircularSubsitesException(mDeclarationName); } mSubsiteHistory.add(mDeclarationName); } // process the site definition SiteProcessor processor = processor_factory.getProcessor(); if (processor != null) { processor.processSite(this, declaration, mResourceFinder); } mIdentifier = processor_factory.getIdentifier(); finish(); mProcessed = true; } private synchronized void finish() throws EngineException { // first gather all the data about the elements and the subsites setupData(); if (null == getParent()) { // process the collected data from the root upwards to ensure // that all elements have been declared before the links to them // are processed processData(); // create the inheritance stacks from the root site upwards // otherwise they would be created before all the subsites are // completely defined createInheritanceStacks(); // create the precedence stacks from the root site upwards // otherwise they would be created before all the subsites are // completely defined createPrecedenceStacks(); } } public SiteBuilder setFallback(String fallbackElementId) { mFallbackElementId = ensureLocalElementId(fallbackElementId); return this; } public SubsiteDeclaration enterSubsiteDeclaration(String declarationName) throws EngineException { // auto-generate empty declaration names if (null == declarationName) { declarationName = SiteProcessorFactory.MANUAL_IDENTIFIER+":"+getDeclarationName()+":subsite"+(mChildSubsiteDeclarations.size()+1); } GroupDeclaration group_declaration = new GroupDeclaration(this, declarationName, getCurrentGroupDeclaration()); SubsiteDeclaration subsite_declaration = new SubsiteDeclaration(declarationName, group_declaration, mStateStores); mChildSubsiteDeclarations.add(subsite_declaration); SiteBuilder builder = new SiteBuilder(subsite_declaration.getDeclarationName(), mResourceFinder, this, subsite_declaration); subsite_declaration.setSiteBuilder(builder); group_declaration.setActiveSiteBuilder(builder); return subsite_declaration; } public SubsiteDeclaration leaveSubsite() { if (null == mParent) { return null; } return mSubsiteDeclaration; } public SiteBuilder enterGroup() throws EngineException { addGroupDeclaration(new GroupDeclaration(this, mDeclarationName, getCurrentGroupDeclaration())); return this; } public SiteBuilder leaveGroup() throws EngineException { mGroupDeclarationsStack.pop(); return this; } public SiteBuilder setInherits(String inherits) throws EngineException { getCurrentGroupDeclaration().setInherits(inherits); return this; } public SiteBuilder setPre(String pre) throws EngineException { getCurrentGroupDeclaration().setPre(pre); return this; } public SiteBuilder addGlobalBean(String classname) { return addGlobalBean(classname, null, null, null); } public SiteBuilder addGlobalBean(String classname, String prefix) { return addGlobalBean(classname, prefix, null, null); } public SiteBuilder addGlobalBean(String classname, String prefix, String name) { return addGlobalBean(classname, prefix, name, null); } public SiteBuilder addGlobalBean(String classname, String prefix, String name, String groupName) throws EngineException { BeanDeclaration bean_declaration = new BeanDeclaration(classname, prefix, groupName); return addGlobalBean(bean_declaration, name); } public SiteBuilder addGlobalBean(Class klass) { return addGlobalBean(klass, null, null, null); } public SiteBuilder addGlobalBean(Class klass, String prefix) { return addGlobalBean(klass, prefix, null, null); } public SiteBuilder addGlobalBean(Class klass, String prefix, String name) { return addGlobalBean(klass, prefix, name, null); } public SiteBuilder addGlobalBean(Class klass, String prefix, String name, String groupName) throws EngineException { BeanDeclaration bean_declaration = new BeanDeclaration(klass, prefix, groupName); return addGlobalBean(bean_declaration, name); } private SiteBuilder addGlobalBean(BeanDeclaration beanDeclaration, String name) throws EngineException { GroupDeclaration site_group = getCurrentGroupDeclaration(); try { Class bean_class = beanDeclaration.getBeanClass(); if (name != null) { site_group.addNamedGlobalBean(name, beanDeclaration); } try { Object instance = bean_class.newInstance(); Constrained constrained = ConstrainedUtils.makeConstrainedInstance(instance); Set<String> properties; if (beanDeclaration.getGroupName() != null) { if (!(instance instanceof ValidatedConstrained)) { throw new GlobalBeanGroupRequiresValidatedConstrainedException(mDeclarationName, beanDeclaration.getClassname(), beanDeclaration.getGroupName()); } ValidatedConstrained validation = (ValidatedConstrained)instance; ValidationGroup group = validation.getGroup(beanDeclaration.getGroupName()); if (null == group) { throw new GlobalBeanGroupNotFoundException(mDeclarationName, beanDeclaration.getClassname(), beanDeclaration.getGroupName()); } properties = new TreeSet<String>(); if (null == beanDeclaration.getPrefix()) { properties.addAll(group.getPropertyNames()); } else { for (String property_name : (List<String>)group.getPropertyNames()) { properties.add(beanDeclaration.getPrefix()+property_name); } } } else { properties = BeanUtils.getPropertyNames(bean_class, null, null, beanDeclaration.getPrefix()); } for (String property : properties) { if (ConstrainedUtils.editConstrainedProperty(constrained, property, beanDeclaration.getPrefix())) { site_group.addGlobalVar(property, new GlobalVar(null)); } } } catch (IllegalAccessException e) { throw new GlobalBeanErrorException(mDeclarationName, beanDeclaration.getClassname(), e); } catch (InstantiationException e) { throw new GlobalBeanErrorException(mDeclarationName, beanDeclaration.getClassname(), e); } catch (BeanUtilsException e) { throw new GlobalBeanErrorException(mDeclarationName, beanDeclaration.getClassname(), e); } } catch (ClassNotFoundException e) { throw new GlobalBeanNotFoundException(mDeclarationName, beanDeclaration.getClassname(), e); } return this; } public SiteBuilder addGlobalExit(String name, String destId) throws EngineException { return addGlobalExit(name, destId, false, false, false, false, false, false); } public SiteBuilder addReflectiveGlobalExit(String name) throws EngineException { return addGlobalExit(name, null, true, false, false, false, false, false); } public SiteBuilder addSnapbackGlobalExit(String name) throws EngineException { return addGlobalExit(name, null, false, true, false, false, false, false); } public SiteBuilder addRedirectGlobalExit(String name) throws EngineException { return addGlobalExit(name, null, false, false, false, false, true, false); } public SiteBuilder addGlobalExit(String name, String destId, boolean reflective, boolean snapback, boolean cancelInheritance, boolean cancelEmbedding, boolean redirect, boolean cancelContinuations) throws EngineException { if (null == destId && !reflective && !snapback) { throw new GlobalExitTargetRequiredException(mDeclarationName, name); } if (null == destId && reflective && snapback) { throw new GlobalExitAmbiguousTargetException(mDeclarationName, name); } if (destId != null && (reflective || snapback)) { throw new GlobalExitAmbiguousTargetException(mDeclarationName, name); } GlobalExit globalexit = new GlobalExit(destId, reflective, snapback, cancelInheritance, cancelEmbedding, redirect, cancelContinuations); getCurrentGroupDeclaration().addGlobalExit(name, globalexit); return this; } public SiteBuilder setArrival(String destId) throws EngineException { return setArrival(destId, false); } public SiteBuilder setArrival(String destId, boolean redirect) throws EngineException { mArrivalElementId = ensureLocalElementId(destId); mArrivalRedirect = redirect; return this; } public SiteBuilder addDeparture(String srcId) throws EngineException { mDepartureIds.add(ensureLocalElementId(srcId)); return this; } public SiteBuilder enterState(String store) throws EngineException { StateStore state_store = StateStoreFactory.getInstance(store); if (null == state_store) { throw new StateStoreUnknownException(mDeclarationName, store); } mStateStores.push(state_store); return this; } public SiteBuilder leaveState() throws EngineException { mStateStores.pop(); return this; } public SiteBuilder addElement(Class klass) throws EngineException { return enterElement(null).setImplementation(klass).leaveElement(); } public ElementInfoBuilder enterElement() throws EngineException { return enterElement(null); } public ElementInfoBuilder enterElement(String declarationName) throws EngineException { ElementDeclaration declaration = new ElementDeclaration(this, mResourceFinder, getCurrentGroupDeclaration(), mStateStores.peek(), declarationName); addElementDeclaration(declaration); return declaration.getElementInfoBuilder(); } public SiteBuilder addGlobalVar(String name) throws EngineException { return addGlobalVar(name, null); } public SiteBuilder addGlobalVar(String name, String[] defaultValues) throws EngineException { getCurrentGroupDeclaration().addGlobalVar(name, new GlobalVar(defaultValues)); return this; } public SiteBuilder addGlobalCookie(String name) throws EngineException { return addGlobalCookie(name, null); } public SiteBuilder addGlobalCookie(String name, String defaultValue) throws EngineException { getCurrentGroupDeclaration().addGlobalCookie(name, defaultValue); return this; } public SiteBuilder addErrorHandler(String destId) throws EngineException { return addErrorHandler(destId, null); } public SiteBuilder addErrorHandler(String destId, Collection<Class> exceptionTypes) throws EngineException { if (null == destId) { throw new ErrorHandlerTargetRequiredException(getDeclarationName()); } getCurrentGroupDeclaration().addErrorHandler(new ErrorHandler(makeAbsoluteElementId(destId), exceptionTypes)); return this; } public SiteBuilder addProperty(String name, Object value) throws EngineException { return addProperty(name, new PropertyValueObject(value)); } public SiteBuilder addProperty(String name, PropertyValue value) { mProperties.put(name, value); return this; } public HierarchicalProperties getProperties() { return mProperties; } public boolean containsProperty(String name) { if (null == mSubsiteDeclaration) { return mProperties.contains(name); } else { return mSubsiteDeclaration.getProperties().contains(name); } } public Object getProperty(String name) { return getProperty(name, null); } public Object getProperty(String name, Object defaultValue) { PropertyValue property = _getProperty(name); Object result = null; if (property != null) { try { result = property.getValue(); } catch (PropertyValueException e) { throw new PropertyValueRetrievalException(getDeclarationName(), name, e); } } if (null == result) { return defaultValue; } return result; } private PropertyValue _getProperty(String name) { if (null == mSubsiteDeclaration) { return mProperties.get(name); } else { return mSubsiteDeclaration.getProperties().get(name); } } public String getPropertyString(String name) { return getPropertyString(name, null); } public String getPropertyString(String name, String defaultValue) { PropertyValue property = _getProperty(name); String result = null; if (property != null) { try { result = property.getValueString(); } catch (PropertyValueException e) { throw new PropertyValueRetrievalException(getDeclarationName(), name, e); } } if (null == result || 0 == result.length()) { return defaultValue; } return result; } public boolean isPropertyEmpty(String name) throws EngineException { if (!containsProperty(name)) { return true; } if (0 == getPropertyString(name, "").length()) { return true; } return false; } private SiteBuilder addGroupDeclaration(GroupDeclaration groupDeclaration) { mGroupDeclarationsStack.push(groupDeclaration); mGroupDeclarations.add(groupDeclaration); return this; } private void addElementDeclaration(ElementDeclaration elementDeclaration) { mElementDeclarations.add(elementDeclaration); } private GroupDeclaration getCurrentGroupDeclaration() { return mGroupDeclarationsStack.peek(); } private String getId() { if (mSiteId != null) { return mSiteId; } if (null == mParent) { mSiteId = "."; } else { mSiteId = getParent().getId()+mSubsiteDeclaration.getId()+"."; } return mSiteId; } private SiteBuilder getRoot() { SiteBuilder current = this; while (current.getParent() != null) { current = current.getParent(); } return current; } SiteBuilder getParent() { return mParent; } private Collection<ElementDeclaration> getElementDeclarations() { return mElementDeclarations; } public SubsiteDeclaration getSubsiteDeclaration() { return mSubsiteDeclaration; } private ElementDeclaration getArrival() { return mArrivalElementDeclaration; } ElementDeclaration getGlobalElementDeclaration(String id) { assert id != null; // relative element id if (!id.startsWith(".")) { id = getId()+id; } id = Site.getCanonicalId(id); // obtain the different parts of the id string List<String> id_parts = StringUtils.split(id, "."); int id_part_counter = 0; SiteBuilder current_site = getRoot(); id_parts.remove(0); // remove the first entry since it is empty (id starts with a dot) for (String id_part : id_parts) { id_part_counter++; // check if this is the last id part, only handle elements // in the last id part, the rest must be subsite ids // it's possible though that elements are injected in subsites // from a parent subsite by specifying the dot-seperate path // directly if (id_part_counter < id_parts.size()) { String element_injection = id.substring(current_site.getId().length()); if (current_site.mElementIdMapping.containsKey(element_injection)) { return current_site.mElementIdMapping.get(element_injection); } // check if the next path matches a subsite if (current_site.mChildSubsiteIdMapping.containsKey(id_part)) { current_site = current_site.mChildSubsiteIdMapping.get(id_part).getSiteBuilder(); } else { return null; } } // last id part, thus also look for elements else { if (current_site.mElementIdMapping.containsKey(id_part)) { return current_site.mElementIdMapping.get(id_part); } if (current_site.mChildSubsiteIdMapping.containsKey(id_part)) { return current_site.mChildSubsiteIdMapping.get(id_part).getSiteBuilder().getArrival(); } } } return null; } private ElementDeclaration getElementDeclaration(String id) { assert id != null; if (mElementIdMapping.containsKey(id)) { return mElementIdMapping.get(id); } if (mChildSubsiteIdMapping.containsKey(id)) { return mChildSubsiteIdMapping.get(id).getSiteBuilder().getArrival(); } return null; } private ElementInfo getElementInfo(String id) { assert id != null; ElementDeclaration element_declaration = getElementDeclaration(id); if (element_declaration != null) { return element_declaration.getElementInfo(); } return null; } private void setupElements() { ElementInfo element_info = null; // iterate over the element declarations for (ElementDeclaration element_declaration : getElementDeclarations()) { // ensure the unique element id if (mElementIdMapping.containsKey(element_declaration.getId())) { throw new ElementIdAlreadyRegisteredToElementException(mDeclarationName, element_declaration.getId(), mElementIdMapping.get(element_declaration.getId()).getDeclarationName(), element_declaration.getDeclarationName()); } if (mChildSubsiteIdMapping.containsKey(element_declaration.getId())) { throw new ElementIdAlreadyRegisteredToSubsiteException(mDeclarationName, element_declaration.getId(), mChildSubsiteIdMapping.get(element_declaration.getId()).getDeclarationName(), element_declaration.getDeclarationName()); } // process the element directives ElementInfoBuilder builder = element_declaration.getElementInfoBuilder(); builder.process(); GroupDeclaration group = element_declaration.getGroup(); element_info = builder.createElementInfo( group.getGlobalExitsMerged(), group.getGlobalVarsMerged(), group.getGlobalCookiesMerged(), group.getNamedGlobalBeansMerged(), group.getErrorHandlersMerged()); element_info.setGroupId(group.getGroupId()); // check if the element declation url ends with a wildcard to add // the support for path info String element_url = element_declaration.getUrl(); if (element_url != null) { if (element_url.endsWith("/") || 0 == element_url.length()) { throw new ElementUrlInvalidException(element_declaration.getId(), element_url); } // ensure an initial slash if (element_url.length() > 0 && !element_url.startsWith("/")) { element_url = "/"+element_url; } // prevent double slashes String url_prefix = getUrlPrefix(); if (url_prefix.endsWith("/") && element_url.startsWith("/")) { url_prefix = StringUtils.stripFromEnd(url_prefix, "/"); element_url = StringUtils.stripFromFront(element_url, "/"); StringBuilder buffer = new StringBuilder(url_prefix); buffer.append("/"); buffer.append(element_url); element_url = buffer.toString(); } else { element_url = url_prefix+element_url; } // handle path info if (element_url.endsWith("/*")) { element_url = element_url.substring(0, element_url.length()-2); element_info.setPathInfoUsed(true); } } // check if the element has an implementation if (null == element_info.getImplementation()) { throw new MissingImplementationException(element_declaration.getDeclarationName()); } // link the element properties with the site properties HierarchicalProperties site_properties; if (null == mSubsiteDeclaration) { site_properties = mProperties; } else { site_properties = mSubsiteDeclaration.getProperties(); } HierarchicalProperties site_properties_shadow = site_properties.createShadow(Rep.getProperties()); // deploy and register the constructed element info element_info.setProperties(element_declaration.getProperties(), site_properties_shadow); element_info.setStateStore(element_declaration.getStateStore()); element_declaration.setElementInfo(element_info); mSite.addElementInfo(getId()+element_declaration.getId(), element_info, element_url); mElementIdMapping.put(element_declaration.getId(), element_declaration); element_info.deploy(); } } private void setupSubsites() { for (SubsiteDeclaration subsite : mChildSubsiteDeclarations) { SiteBuilder subsite_builder = subsite.getSiteBuilder(); subsite_builder.process(); // register the subsite id if (mElementIdMapping.containsKey(subsite.getId())) { throw new SubsiteIdAlreadyRegisteredToElementException(mDeclarationName, subsite.getId(), mElementIdMapping.get(subsite.getId()).getDeclarationName(), subsite.getDeclarationName()); } if (mChildSubsiteIdMapping.containsKey(subsite.getId())) { throw new SubsiteIdAlreadyRegisteredToSubsiteException(mDeclarationName, subsite.getId(), mChildSubsiteIdMapping.get(subsite.getId()).getDeclarationName(), subsite.getDeclarationName()); } // register the subsite id mChildSubsiteIdMapping.put(subsite.getId(), subsite); } } private void setupArrivalElement() { if (mArrivalElementId != null) { mArrivalElementDeclaration = getElementDeclaration(mArrivalElementId); if (null == mArrivalElementDeclaration) { elementIdNotFound(mArrivalElementId); } // clone the element declaration and store it in the collection of the // site's element declarations mArrivalElementDeclaration = mArrivalElementDeclaration.clone(); mArrivalElementDeclaration.setId(""); addElementDeclaration(mArrivalElementDeclaration); mElementIdMapping.put("", mArrivalElementDeclaration); } } private void processSubsites() { for (SubsiteDeclaration subsite : mChildSubsiteDeclarations) { subsite.getSiteBuilder().processData(); } } private void processGlobalExits() { ElementInfo element_info = null; // iterate over all the element declarations in the site for (ElementDeclaration element_declaration : getElementDeclarations()) { element_info = element_declaration.getElementInfo(); // iterate over all global exits of the element Map<String, GlobalExit> global_exits = element_declaration.getGroup().getGlobalExitsMerged(); String global_exit_name = null; GlobalExit global_exit = null; FlowLink flowlink = null; for (Map.Entry<String, GlobalExit> global_exit_entry : global_exits.entrySet()) { global_exit_name = global_exit_entry.getKey(); global_exit = global_exit_entry.getValue(); // add the exit to every element info element_info.addExit(global_exit_name); // ensure a flow link from each elementinfo to the target element id HashSet<FlowLinkDeclaration> flowlinks = mFlowLinkMapping.get(element_declaration.getId()); if (null == flowlinks) { flowlinks = new HashSet<FlowLinkDeclaration>(); mFlowLinkMapping.put(element_declaration.getId(), flowlinks); } // check if this points to a specific element or if it's reflective if (global_exit.isReflective()) { flowlink = new FlowLink(global_exit_name, element_info, global_exit.isSnapback(), global_exit.cancelInheritance(), global_exit.cancelEmbedding(), global_exit.isRedirect(), global_exit.cancelContinuations()); element_info.setFlowLink(flowlink); // check if this element has identical inputs and outputs // these will be automatically linked due to the reflective exit Collection<String> input_names = element_info.getInputNames(); Collection<String> output_names = element_info.getOutputNames(); DataLinkDeclaration datalink = null; for (String input_name : input_names) { if (output_names.contains(input_name)) { datalink = new DataLinkDeclaration(input_name, null, element_declaration.getId(), false, input_name, null, null); addDataLink(element_declaration.getId(), datalink, false); } } } else { ElementDeclaration target_elementdeclaration = null; String globalexit_destid = global_exit.getDestId(); if (globalexit_destid != null) { // get the element declaration that corresponds to the destination id target_elementdeclaration = getGlobalElementDeclaration(globalexit_destid); // if the target element couldn't be found, throw an exception if (null == target_elementdeclaration) { elementIdNotFound(globalexit_destid); } global_exit.setTarget(target_elementdeclaration.getElementInfo()); } flowlinks.add(new FlowLinkDeclaration(this, global_exit_name, globalexit_destid, global_exit.isSnapback(), global_exit.cancelInheritance(), global_exit.cancelEmbedding(), global_exit.isRedirect(), global_exit.cancelContinuations())); } } } } void elementIdNotFound(String elementId) { if (mChildSubsiteIdMapping.containsKey(elementId)) { throw new ElementIdNotFoundSiteIdExistsException(mSiteId, elementId); } throw new ElementIdNotFoundInSiteException(mSiteId, elementId); } private void processAutoLinks() { // process the auto links HashSet<AutoLinkDeclaration> autolinks = null; for (String current_elementid : mAutoLinkMapping.keySet()) { autolinks = mAutoLinkMapping.get(current_elementid); for (AutoLinkDeclaration autolink_declaration : autolinks) { autolink_declaration.registerFlowAndDataLinks(); } } } private void processFlowLinks() { ElementDeclaration current_elementdeclaration = null; // process the flow links HashSet<FlowLinkDeclaration> flowlinks = null; for (String current_elementid : mFlowLinkMapping.keySet()) { current_elementdeclaration = getElementDeclaration(current_elementid); flowlinks = mFlowLinkMapping.get(current_elementid); for (FlowLinkDeclaration flowlink_declaration : flowlinks) { // add the flowlink to the element info current_elementdeclaration.getElementInfo().setFlowLink(flowlink_declaration.getFlowLink()); } } } private void processDataLinks() { ElementDeclaration current_elementdeclaration = null; ElementDeclaration target_elementdeclaration = null; // process the data links HashSet<DataLinkDeclaration> datalinks = null; String datalink_destid = null; DataLink datalink = null; for (String current_elementid : mDataLinkMapping.keySet()) { current_elementdeclaration = getElementDeclaration(current_elementid); datalinks = mDataLinkMapping.get(current_elementid); for (DataLinkDeclaration datalink_declaration : datalinks) { // resolve target element declaration if (datalink_declaration.isSnapback()) { target_elementdeclaration = current_elementdeclaration; } else { datalink_destid = datalink_declaration.getDestId(); // get the element declaration that corresponds to the destination id target_elementdeclaration = getGlobalElementDeclaration(datalink_destid); // if the target element couldn't be found, throw an exception if (null == target_elementdeclaration) { elementIdNotFound(datalink_declaration.getDestId()); } } // handle bean data transfers if (datalink_declaration.transfersBean()) { if (!current_elementdeclaration.getElementInfo().containsNamedOutbean(datalink_declaration.getSrcOutbean())) { throw new DataLinkUnknownSrcOutbeanException(mDeclarationName, datalink_declaration.getSrcOutbean(), current_elementid, datalink_declaration.getDestId(), datalink_declaration.isSnapback()); } if (!target_elementdeclaration.getElementInfo().containsNamedInbean(datalink_declaration.getDestInbean())) { throw new DataLinkUnknownDestInbeanException(mDeclarationName, datalink_declaration.getDestInbean(), current_elementid, datalink_declaration.getDestId(), datalink_declaration.isSnapback()); } BeanDeclaration srcoutbean = current_elementdeclaration.getElementInfo().getNamedOutbeanInfo(datalink_declaration.getSrcOutbean()); BeanDeclaration destinbean = target_elementdeclaration.getElementInfo().getNamedInbeanInfo(datalink_declaration.getDestInbean()); Class srcoutbean_class = null; Class destinbean_class = null; try { srcoutbean_class = Class.forName(srcoutbean.getClassname()); destinbean_class = Class.forName(destinbean.getClassname()); } catch (ClassNotFoundException e) { throw new DataLinkBeanErrorException(mDeclarationName, datalink_declaration.getSrcOutbean(), current_elementid, datalink_declaration.getDestId(), datalink_declaration.isSnapback(), datalink_declaration.getDestInbean(), e); } Constrained constrained_destinbean = ConstrainedUtils.getConstrainedInstance(destinbean_class); Set<String> srcoutbean_properties = null; Set<String> destinbean_properties = null; try { srcoutbean_properties = BeanUtils.getPropertyNames(srcoutbean_class, null, null, srcoutbean.getPrefix()); destinbean_properties = BeanUtils.getPropertyNames(destinbean_class, null, null, destinbean.getPrefix()); } catch (BeanUtilsException e) { throw new DataLinkBeanErrorException(mDeclarationName, datalink_declaration.getSrcOutbean(), current_elementid, datalink_declaration.getDestId(), datalink_declaration.isSnapback(), datalink_declaration.getDestInbean(), e); } for (String srcoutbean_property : srcoutbean_properties) { datalink = null; if (destinbean_properties.contains(srcoutbean_property) && ConstrainedUtils.editConstrainedProperty(constrained_destinbean, srcoutbean_property, srcoutbean.getPrefix()) && current_elementdeclaration.getElementInfo().containsOutputPossibility(srcoutbean_property)) { if (datalink_declaration.isSnapback()) { datalink = new DataLink(srcoutbean_property, null, datalink_declaration.isSnapback(), srcoutbean_property, datalink_declaration.getFlowLink()); current_elementdeclaration.getElementInfo().addDataLink(datalink); } else if (target_elementdeclaration.getElementInfo().containsInputPossibility(srcoutbean_property)) { datalink = new DataLink(srcoutbean_property, target_elementdeclaration.getElementInfo(), datalink_declaration.isSnapback(), srcoutbean_property, datalink_declaration.getFlowLink()); current_elementdeclaration.getElementInfo().addDataLink(datalink); } } } } // handle regular values transfer else { if (datalink_declaration.isSnapback()) { datalink = new DataLink(datalink_declaration.getSrcOutput(), null, datalink_declaration.isSnapback(), datalink_declaration.getDestInput(), datalink_declaration.getFlowLink()); } else { datalink = new DataLink(datalink_declaration.getSrcOutput(), target_elementdeclaration.getElementInfo(), false, datalink_declaration.getDestInput(), datalink_declaration.getFlowLink()); } current_elementdeclaration.getElementInfo().addDataLink(datalink); } } } } private void processFallbackElement() { if (mFallbackElementId != null) { ElementInfo fallback_elementinfo = getElementInfo(mFallbackElementId); if (null == fallback_elementinfo) { elementIdNotFound(mFallbackElementId); } mSite.addFallback(fallback_elementinfo, getUrlPrefix()); } } private void processArrivalElement() { if (mArrivalElementDeclaration != null) { // this second pass ensures that the arrival element is linked to and from // the correct locations ElementDeclaration target_element_declaration = getElementDeclaration(mArrivalElementId); mArrivalElementDeclaration.getElementInfo().populateFrom(target_element_declaration.getElementInfo()); mArrivalElementDeclaration.setGroup(target_element_declaration.getGroup()); // ensure & valid arrival id String arrival_id = mSiteId; // only use the site's url prefix if it's the root site or a subsite // that has its own url prefix String arrival_url = null; if (null == mParent || target_element_declaration.getUrl() != null || !getUrlPrefix().equals(mParent.getUrlPrefix())) { arrival_url = getUrlPrefix(); // an arrival should never end with a final slash since that's // taken care of by the engine gate if (arrival_url.endsWith("/")) { arrival_url = StringUtils.stripFromEnd(arrival_url, "/"); } } // handle arrival redirects if (mArrivalRedirect) { mArrivalElementDeclaration.getElementInfo().setImplementation(Redirect.class.getName()); HierarchicalProperties properties = mArrivalElementDeclaration.getElementInfo().getProperties(); properties.put("redirectInputs", true); properties.put("redirectInputs", true); // handle redirects when the target element has no URL if (null == target_element_declaration.getUrl()) { properties.put("to", arrival_url+"/"); properties.put("type", "url"); } else { properties.put("to", makeAbsoluteElementId(mArrivalElementId)); properties.put("type", "element"); } } // register the arrival element ElementInfo arrival_elementinfo = mArrivalElementDeclaration.getElementInfo(); arrival_elementinfo.setId(null); arrival_elementinfo.setUrl(null); mSite.addElementInfo(arrival_id, arrival_elementinfo, arrival_url); // ensure that the empty root url isn't mapped twice if (arrival_url != null && arrival_url.length() > 0) { // if the arrival redirects, and the target element has got no URL, // ensure that the URL with a final slash doesn't redirect to itself, // just map the arrival target element to the URL instead if (mArrivalRedirect && null == target_element_declaration.getUrl()) { mSite.mapElementId(makeAbsoluteElementId(mArrivalElementId), arrival_url+"/"); } // map the URL with a final slash to the same arrival element else { mSite.mapElementId(arrival_id, arrival_url+"/"); } } } } private void processDepartureElements() { if (mDepartureIds.size() > 0) { ElementDeclaration departure_elementdeclaration = null; for (String departure_id : mDepartureIds) { departure_elementdeclaration = getElementDeclaration(departure_id); if (null == departure_elementdeclaration) { elementIdNotFound(departure_id); } ArrayList<String> departure_vars = new ArrayList<String>(departure_elementdeclaration.getGroup().getGlobalVarsLocal().keySet()); departure_elementdeclaration.getElementInfo().setDepartureVars(departure_vars); } } } private void processInheritsStack() { String parent_id = null; ElementDeclaration parent_elementdeclaration = null; // get the parent elementdeclaration of the elements that inherit from another element for (ElementDeclaration element_declaration : getElementDeclarations()) { // obtain the parent elementdeclaration parent_id = element_declaration.getInherits(); if (parent_id != null) { parent_elementdeclaration = element_declaration.getSiteBuilder().getGlobalElementDeclaration(parent_id); if (null == parent_elementdeclaration) { elementIdNotFound(parent_id); } // add it to the parent stack element_declaration.getParentStack().push(parent_elementdeclaration); } } // add the global parents of groups to the elements contained within if (mGroupDeclarations.size() > 0) { Stack<ArrayList<GroupDeclaration>> groupdeclarations_stack = null; GroupDeclaration child_groupdeclaration = null; ArrayList<GroupDeclaration> child_groupdeclarations = null; ListIterator<GroupDeclaration> groupdeclarations_it = mGroupDeclarations.listIterator(mGroupDeclarations.size()); while (groupdeclarations_it.hasPrevious()) { GroupDeclaration groupdeclaration = groupdeclarations_it.previous(); // obtain the parent elementdeclaration parent_id = groupdeclaration.getInherits(); if (parent_id != null) { parent_elementdeclaration = groupdeclaration.getDeclaringSiteBuilder().getGlobalElementDeclaration(parent_id); if (null == parent_elementdeclaration) { elementIdNotFound(parent_id); } // add it to the stack of all the elements in the group and its groups groupdeclarations_stack = new Stack<ArrayList<GroupDeclaration>>(); child_groupdeclarations = new ArrayList<GroupDeclaration>(); child_groupdeclarations.add(groupdeclaration); groupdeclarations_stack.push(child_groupdeclarations); // continue until the stack is empty while (groupdeclarations_stack.size() > 0) { // get the top of the stack and process all its groups child_groupdeclarations = groupdeclarations_stack.pop(); while (child_groupdeclarations.size() > 0) { // add the global parent to all the elements within the group child_groupdeclaration = child_groupdeclarations.remove(0); for (ElementDeclaration child_elementdeclaration : child_groupdeclaration.getElementDeclarations()) { child_elementdeclaration.getParentStack().push(parent_elementdeclaration); } // if the group contain other groups, add the collection to the stack if (child_groupdeclaration.getChildGroupDeclarations().size() > 0) { groupdeclarations_stack.push(new ArrayList<GroupDeclaration>(child_groupdeclaration.getChildGroupDeclarations())); } } } } } } } private void processPreStack() { String pre_id = null; ElementDeclaration pre_elementdeclaration = null; // get the pre elementdeclaration of the elements that inherit from another element for (ElementDeclaration element_declaration : getElementDeclarations()) { // obtain the pre elementdeclaration pre_id = element_declaration.getPre(); if (pre_id != null) { pre_elementdeclaration = element_declaration.getSiteBuilder().getGlobalElementDeclaration(pre_id); if (null == pre_elementdeclaration) { elementIdNotFound(pre_id); } // add it to the pre stack element_declaration.getPreStack().push(pre_elementdeclaration); } } // add the global pres of groups to the elements contained within if (mGroupDeclarations.size() > 0) { Stack<ArrayList<GroupDeclaration>> groupdeclarations_stack = null; GroupDeclaration child_groupdeclaration = null; ArrayList<GroupDeclaration> child_groupdeclarations = null; ListIterator<GroupDeclaration> groupdeclarations_it = mGroupDeclarations.listIterator(mGroupDeclarations.size()); while (groupdeclarations_it.hasPrevious()) { GroupDeclaration groupdeclaration = groupdeclarations_it.previous(); // obtain the pre elementdeclaration pre_id = groupdeclaration.getPre(); if (pre_id != null) { pre_elementdeclaration = groupdeclaration.getDeclaringSiteBuilder().getGlobalElementDeclaration(pre_id); if (null == pre_elementdeclaration) { elementIdNotFound(pre_id); } // add it to the stack of all the elements in the group and its groups groupdeclarations_stack = new Stack<ArrayList<GroupDeclaration>>(); child_groupdeclarations = new ArrayList<GroupDeclaration>(); child_groupdeclarations.add(groupdeclaration); groupdeclarations_stack.push(child_groupdeclarations); // continue until the stack is empty while (groupdeclarations_stack.size() > 0) { // get the top of the stack and process all its groups child_groupdeclarations = groupdeclarations_stack.pop(); while (child_groupdeclarations.size() > 0) { // add the global pre to all the elements within the group child_groupdeclaration = child_groupdeclarations.remove(0); for (ElementDeclaration child_elementdeclaration : child_groupdeclaration.getElementDeclarations()) { child_elementdeclaration.getPreStack().push(pre_elementdeclaration); } // if the group contain other groups, add the collection to the stack if (child_groupdeclaration.getChildGroupDeclarations().size() > 0) { groupdeclarations_stack.push(new ArrayList<GroupDeclaration>(child_groupdeclaration.getChildGroupDeclarations())); } } } } } } } private void createInheritanceStacks() { // create the inheritance stack of the elements of subsites first for (SubsiteDeclaration subsitedeclaration : mChildSubsiteDeclarations) { subsitedeclaration.getSiteBuilder().createInheritanceStacks(); } // create the inheritance stack of all the elements in this subsite // construct each element's inheritance stack Stack<ElementInfo> inheritance_stack = null; String parent_id = null; for (ElementDeclaration current_elementdeclaration : getElementDeclarations()) { if (current_elementdeclaration.getParentStack().size() > 0) { inheritance_stack = new Stack<ElementInfo>(); // the element itself should be at the bottom of inheritance stack inheritance_stack.push(current_elementdeclaration.getElementInfo()); // iterate over all the elements in the parent stack for (ElementDeclaration parent_elementdeclaration : current_elementdeclaration.getParentStack()) { // lookup and resolve the parent hierarchy of an element in the parent stack while (parent_elementdeclaration != null) { inheritance_stack.push(parent_elementdeclaration.getElementInfo()); parent_id = parent_elementdeclaration.getInherits(); if (null == parent_id) { parent_elementdeclaration = null; } else { parent_elementdeclaration = parent_elementdeclaration.getSiteBuilder().getGlobalElementDeclaration(parent_id); } } } // store the inheritance stack current_elementdeclaration.getElementInfo().setInheritanceStack(inheritance_stack); } } } private void createPrecedenceStacks() { // create the precedence stack of the elements of subsites first for (SubsiteDeclaration subsitedeclaration : mChildSubsiteDeclarations) { subsitedeclaration.getSiteBuilder().createPrecedenceStacks(); } // create the precedence stack of all the elements in this subsite // construct each element's precedence stack Stack<ElementInfo> precedence_stack = null; for (ElementDeclaration current_elementdeclaration : getElementDeclarations()) { if (current_elementdeclaration.getPreStack().size() > 0) { precedence_stack = new Stack<ElementInfo>(); // the element itself should be at the bottom of precedence stack precedence_stack.push(current_elementdeclaration.getElementInfo()); // iterate over all the elements in the pre stack and resolve them to element infos for (ElementDeclaration pre_elementdeclaration : current_elementdeclaration.getPreStack()) { precedence_stack.push(pre_elementdeclaration.getElementInfo()); } // store the precedence stack current_elementdeclaration.getElementInfo().setPrecedenceStack(precedence_stack); } } } private void processErrorHandlers() { // iterate over all the element declarations in the site for (ElementDeclaration element_declaration : getElementDeclarations()) { // iterate over all error handlers of the element List<ErrorHandler> error_handlers = element_declaration.getGroup().getErrorHandlersMerged(); for (ErrorHandler error_handler : error_handlers) { ElementDeclaration target_elementdeclaration = null; String errorhandler_destid = error_handler.getDestId(); // get the element declaration that corresponds to the destination id target_elementdeclaration = getGlobalElementDeclaration(errorhandler_destid); // if the target element couldn't be found, throw an exception if (null == target_elementdeclaration) { elementIdNotFound(errorhandler_destid); } error_handler.setTarget(target_elementdeclaration.getElementInfo()); } } } private void setupData() { setupElements(); setupSubsites(); setupArrivalElement(); } private void processData() { processSubsites(); processGlobalExits(); processAutoLinks(); processFlowLinks(); processDataLinks(); processFallbackElement(); processArrivalElement(); processDepartureElements(); processInheritsStack(); processPreStack(); processErrorHandlers(); // clean the temporary data mArrivalElementId = null; mDepartureIds = null; mDataLinkMapping = null; mFlowLinkMapping = null; mAutoLinkMapping = null; } private String getUrlPrefix() { if (mUrlPrefix != null) { return mUrlPrefix; } if (null == mParent) { mUrlPrefix = "/"; } else { String url_prefix = getParent().getUrlPrefix(); if (mSubsiteDeclaration.getUrlPrefix() != null) { // prevent double slashes String subsite_url_prefix = mSubsiteDeclaration.getUrlPrefix(); if (url_prefix.endsWith("/") && subsite_url_prefix.startsWith("/")) { url_prefix = StringUtils.stripFromEnd(url_prefix, "/"); subsite_url_prefix = StringUtils.stripFromFront(subsite_url_prefix, "/"); StringBuilder buffer = new StringBuilder(url_prefix); buffer.append("/"); buffer.append(subsite_url_prefix); url_prefix = buffer.toString(); } else if (url_prefix.endsWith("/") || subsite_url_prefix.startsWith("/")) { url_prefix = url_prefix+subsite_url_prefix; } else { StringBuilder buffer = new StringBuilder(url_prefix); buffer.append("/"); buffer.append(subsite_url_prefix); url_prefix = buffer.toString(); } } mUrlPrefix = url_prefix; } return mUrlPrefix; } String getDeclarationName() { return mDeclarationName; } void addDataLink(String srcId, DataLinkDeclaration dataLink, boolean overrideExisting) { HashSet<DataLinkDeclaration> datalinks = mDataLinkMapping.get(srcId); if (null == datalinks) { datalinks = new HashSet<DataLinkDeclaration>(); mDataLinkMapping.put(srcId, datalinks); } if (overrideExisting || !datalinks.contains(dataLink)) { datalinks.add(dataLink); } } void addFlowLink(String srcId, FlowLinkDeclaration flowLink) { HashSet<FlowLinkDeclaration> flowlinks = mFlowLinkMapping.get(srcId); if (null == flowlinks) { flowlinks = new HashSet<FlowLinkDeclaration>(); mFlowLinkMapping.put(srcId, flowlinks); } flowlinks.add(flowLink); } void addAutoLink(String srcId, AutoLinkDeclaration autoLink) { HashSet<AutoLinkDeclaration> autolinks = mAutoLinkMapping.get(srcId); if (null == autolinks) { autolinks = new HashSet<AutoLinkDeclaration>(); mAutoLinkMapping.put(srcId, autolinks); } autolinks.add(autoLink); } String ensureLocalElementId(String id) throws EngineException { if (id != null) { if (id.indexOf(".") != -1 || id.indexOf("^") != -1) { throw new LocalElementIdRequiredException(id); } } return id; } String makeAbsoluteElementId(String id) { if (null == id) { return null; } if (!id.startsWith(".")) { id = getId()+id; } return id; } static String generateId(String declarationName) { if (null == declarationName) { return null; } String id = FileUtils.getBaseName(declarationName); if (null == id) { id = declarationName; } int index = id.lastIndexOf("/"); if (-1 == index) { index = id.lastIndexOf(":"); } if (index != -1) { if (index == id.length()-1) { id = null; } else { id = id.substring(index+1); } } return id; } public SiteBuilder addResourceModificationTime(UrlResource resource, long modificationTime) { if (RifeConfig.Engine.getSiteAutoReload()) { mSite.addResourceModificationTime(resource, modificationTime); } return this; } }