/** Copyright (C) 2012 Delcyon, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.delcyon.capo.resourcemanager.types; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Stack; import java.util.Vector; import java.util.logging.Level; import org.w3c.dom.Document; import org.w3c.dom.Element; import com.delcyon.capo.CapoApplication; import com.delcyon.capo.controller.ControlElement; import com.delcyon.capo.datastream.NullOutputStream; import com.delcyon.capo.datastream.StreamEventFilterOutputStream; import com.delcyon.capo.datastream.StreamEventListener; import com.delcyon.capo.datastream.StreamUtil; import com.delcyon.capo.resourcemanager.ContentFormatType; import com.delcyon.capo.resourcemanager.ResourceDescriptor; import com.delcyon.capo.resourcemanager.ResourceParameter; import com.delcyon.capo.resourcemanager.ResourceParameter.EvaluationContext; import com.delcyon.capo.resourcemanager.ResourceParameter.Source; import com.delcyon.capo.resourcemanager.ResourceType; import com.delcyon.capo.resourcemanager.ResourceURI; import com.delcyon.capo.server.CapoServer; import com.delcyon.capo.util.ReflectionUtility; import com.delcyon.capo.xml.XPath; import com.delcyon.capo.xml.cdom.CDocument; import com.delcyon.capo.xml.cdom.CElement; import com.delcyon.capo.xml.cdom.VariableContainer; import com.delcyon.capo.xml.dom.ResourceDeclarationElement; /** * @author jeremiah TODO verify required parameters and values */ public abstract class AbstractResourceDescriptor implements ResourceDescriptor { private HashMap<String, String> declaredParameterHashMap = new HashMap<String, String>(); // NOW eval context in ResourceElement private HashMap<String, String> delayedParameterHashMap = new HashMap<String, String>(); // private HashMap<String, String> contextParameterHashMap = new HashMap<String, String>(); private HashMap<StreamType, State> streamStateHashMap = new HashMap<StreamType, State>(); private ResourceURI resourceURI = null; private ResourceParameter[] initialResourceParameters = null; private HashMap<State, StateParameters> stateParametersHashMap = new HashMap<State, StateParameters>(); private HashMap<String, ResourceDescriptor> childResourceDescriptorHashMap = new HashMap<String, ResourceDescriptor>(); private List<ContentMetaData> childContentMetaDataList = null; private LifeCycle lifeCycle; private LifeCycle originallyDeclaredLifeCycle = null; //needed for re-initialization private State resourceState = State.NONE; private boolean isIterating = false; private ResourceType resourceType; private VariableContainer declaringVariableContainer; private OutputStreamTranslater outputStreamTranslater; private Vector<OutputStream> openOutputStreamVector = new Vector<OutputStream>(); private Vector<InputStream> openInputStreamVector = new Vector<InputStream>(); private String localName = null; private ResourceDeclarationElement declaringResourceElement; private ContentMetaData resourceMetaData; private transient ResourceDescriptor parentResourceDescriptor; @Override public void setup(ResourceType resourceType, String resourceURI) throws Exception { this.resourceState = State.NONE; this.resourceType = resourceType; this.resourceURI = new ResourceURI(resourceURI); this.lifeCycle = resourceType.getDefaultLifeCycle(); } @Override public void init(ResourceDeclarationElement declaringResourceElement, VariableContainer variableContainer, LifeCycle lifeCycle, boolean iterate, ResourceParameter... resourceParameters) throws Exception { this.declaringResourceElement = declaringResourceElement; this.declaringVariableContainer = variableContainer; // override life cycle if (lifeCycle == null) { this.lifeCycle = resourceType.getDefaultLifeCycle(); } else { this.originallyDeclaredLifeCycle = lifeCycle; this.lifeCycle = lifeCycle; } if (iterate == true) { isIterating = true; } // process resource parameters initialResourceParameters = resourceParameters; // store parameters from URI declaredParameterHashMap.putAll(resourceURI.getParameterMap()); for (ResourceParameter resourceParameter : initialResourceParameters) { if (resourceParameter.getEvaluationContext() == EvaluationContext.NOW) { declaredParameterHashMap.put(resourceParameter.getName(), processVars(variableContainer, resourceParameter.getValue())); } else if (resourceParameter.getEvaluationContext() == EvaluationContext.DELAYED) { delayedParameterHashMap.put(resourceParameter.getName(), resourceParameter.getValue()); } } this.resourceState = State.INITIALIZED; this.stateParametersHashMap.put(State.INITIALIZED, new StateParameters(resourceParameters, variableContainer)); } @Override public void open(VariableContainer variableContainer, ResourceParameter... resourceParameters) throws Exception { if (resourceState == State.NONE) { init(null, variableContainer, null, false, resourceParameters); } if (resourceState == State.STEPPING) { return; } if (resourceState == State.OPEN) { refreshResourceMetaData(variableContainer, resourceParameters); clearContent(); return; } addResourceParameters(variableContainer, resourceParameters); refreshResourceMetaData(variableContainer, resourceParameters); clearContent(); this.resourceState = State.OPEN; this.stateParametersHashMap.put(State.OPEN, new StateParameters(resourceParameters, variableContainer)); } protected abstract void clearContent() throws Exception; protected abstract ContentMetaData buildResourceMetaData(VariableContainer variableContainer, ResourceParameter... resourceParameters) throws Exception; protected void refreshResourceMetaData(VariableContainer variableContainer, ResourceParameter... resourceParameters) throws Exception { if (this.resourceMetaData != null && this.resourceMetaData instanceof SimpleContentMetaData == false) { this.resourceMetaData.refresh(resourceParameters); } else { this.resourceMetaData = buildResourceMetaData(variableContainer, resourceParameters); } } @Override public ContentMetaData getResourceMetaData(VariableContainer variableContainer, ResourceParameter... resourceParameters) throws Exception { advanceState(State.OPEN, variableContainer, resourceParameters); //System.out.println("hmmm"); //This currently seems unneeded and is about as expensive a call as possible esp when reading a file system // if (resourceMetaData != null) // { // if (resourceMetaData.isDynamic()) // { // refreshResourceMetaData(variableContainer, resourceParameters); // } // } if (resourceMetaData.isInitialized() == false) { resourceMetaData.init(); } return resourceMetaData; } @Override public boolean isRemoteResource() { return false; } @Override public void close(VariableContainer variableContainer, ResourceParameter... resourceParameters) throws Exception { Vector<ResourceParameter> tempResourceParameterVector = new Vector<ResourceParameter>(); for (ResourceParameter resourceParameter : resourceParameters) { if (resourceParameter.getSource() == Source.DECLARATION) { tempResourceParameterVector.add(resourceParameter); } } if (outputStreamTranslater != null) { outputStreamTranslater.close(); outputStreamTranslater = null; } if (isIterating() == false) { resourceState = State.CLOSED; this.stateParametersHashMap.put(State.CLOSED, new StateParameters(resourceParameters, variableContainer)); } for (InputStream inputStream : openInputStreamVector) { try { inputStream.close(); } catch (Exception e) { CapoApplication.logger.log(Level.WARNING, "Error closing inputstream", e); } } openInputStreamVector.clear(); for (OutputStream outputStream : openOutputStreamVector) { try { outputStream.flush(); outputStream.close(); } catch (Exception e) { CapoApplication.logger.log(Level.WARNING, "Error closing outputstream", e); } } openOutputStreamVector.clear(); } // TODO /* * cleanup should remove call parameters depending on lifeCycle destroy may want to be called destroy should call close depending destroy should only be called when a resourceElement falls out of scope. */ @Override public void release(VariableContainer variableContainer, ResourceParameter... resourceParameters) throws Exception { if (getResourceState().ordinal() >= State.OPEN.ordinal() && getResourceState().ordinal() < State.CLOSED.ordinal()) { close(variableContainer, resourceParameters); } // TODO cleanup metadata childResourceDescriptorHashMap.clear(); childContentMetaDataList = null; setResourceState(State.RELEASED); this.stateParametersHashMap.put(State.RELEASED, new StateParameters(resourceParameters, variableContainer)); } @Override public void reset(State previousState) throws Exception { if (previousState.ordinal() > getResourceState().ordinal()) { throw new Exception("Cannot reset a resource to a later state. Current State:" + getResourceState() + " Requested State:" + previousState); } // if we aren't already open, don't try, we're probably just trying to re-initialize if (getResourceState().ordinal() < State.OPEN.ordinal()) { release(null); } if (previousState.ordinal() == getResourceState().ordinal()) { nextState(null); } while (getResourceState() != previousState) { int nextState = getResourceState().ordinal() + 1; if (nextState >= State.values().length) { nextState = 0; } StateParameters nextStateParameters = stateParametersHashMap.get(State.values()[nextState]); if (nextStateParameters == null) { nextState(null); } else { nextState(nextStateParameters.getVariableContainer(), nextStateParameters.getResourceParameters()); } } } /** * This is a utility method for subclases to move the resource forward to the desired state * * @param desiredState * @param variableContainer * @param resourceParameters * @throws Exception */ @Override public void advanceState(State desiredState, VariableContainer variableContainer, ResourceParameter... resourceParameters) throws Exception { while (getResourceState().ordinal() < desiredState.ordinal()) { nextState(variableContainer, resourceParameters); } } /** This is just a simple mapping to allow us to jump to the next state. Maybe someday, we can put the proper method pointers in the enum declaration when available. */ private void nextState(VariableContainer variableContainer, ResourceParameter... resourceParameters) throws Exception { if (getResourceState() == State.NONE) { //if original is null, then init should just pick pick up the default init(null, variableContainer, originallyDeclaredLifeCycle, isIterating, resourceParameters); } else if (getResourceState() == State.INITIALIZED) { open(variableContainer, resourceParameters); } else if (getResourceState() == State.OPEN) { next(variableContainer, resourceParameters); } else if (getResourceState() == State.STEPPING) { close(variableContainer, resourceParameters); } else if (getResourceState() == State.CLOSED) { release(variableContainer, resourceParameters); } else if (getResourceState() == State.RELEASED) { setup(resourceType, resourceURI.getResourceURIString()); } } protected ResourceDeclarationElement getDeclaringResourceElement() { return this.declaringResourceElement; } public ResourceURI getResourceURI() { return resourceURI; } protected void setResourceURI(ResourceURI resourceURI) { this.resourceURI = resourceURI; } /** returns last piece of URI **/ @Override public String getLocalName() { if (localName != null) { return this.localName; } else { if(this.resourceURI.getPath().equals("/")) { return this.resourceURI.getPath(); } else if(this.resourceURI.getPath().equals("///")) { return "/"; } else { String[] splitURI = this.resourceURI.getPath().split("/"); return splitURI[splitURI.length - 1]; } } } public void setLocalName(String localName) { this.localName = localName; } @Override public ResourceType getResourceType() { return this.resourceType; } protected void setResourceType(ResourceType resourceType) { this.resourceType = resourceType; } public VariableContainer getDeclaringVariableContainer() { return declaringVariableContainer; } @Override public void addResourceParameters(VariableContainer variableContainer, ResourceParameter... resourceParameters) throws Exception { for (ResourceParameter resourceParameter : resourceParameters) { contextParameterHashMap.put(resourceParameter.getName(), processVars(variableContainer, resourceParameter.getValue())); // this is not right } } public String processVars(VariableContainer variableContainer, String varString) throws Exception { StringBuffer stringBuffer = new StringBuffer(varString); processVars(variableContainer, stringBuffer); return stringBuffer.toString(); } /** * Check String for variables and replace them with the value of the var * * @param varStringBuffer */ private void processVars(VariableContainer variableContainer, StringBuffer varStringBuffer) throws Exception { while (varStringBuffer != null && varStringBuffer.toString().matches(".*\\$\\{.+}.*")) { CapoServer.logger.log(Level.FINE, "found var in '" + varStringBuffer + "'"); Stack<StringBuffer> stack = new Stack<StringBuffer>(); StringBuffer currentStringBuffer = new StringBuffer(); for (int index = 0; index < varStringBuffer.length(); index++) { if (varStringBuffer.charAt(index) == '$' && varStringBuffer.charAt(index + 1) == '{') { stack.push(currentStringBuffer); currentStringBuffer = new StringBuffer(); currentStringBuffer.append(varStringBuffer.charAt(index)); } else if (varStringBuffer.charAt(index) == '}' && varStringBuffer.charAt(index - 1) != '\\' && stack.empty() == false) { // pop, and evaluate currentStringBuffer.append(varStringBuffer.charAt(index)); String varName = currentStringBuffer.toString().replaceFirst(".*\\$\\{(.+)}.*", "$1"); String value = getVarValue(variableContainer, varName); if (value == null) { value = "";// TODO make this configurable to null,exception,or empty CapoServer.logger.log(Level.WARNING, "var '" + varName + "' not found replaced with empty string"); } currentStringBuffer = stack.pop(); currentStringBuffer.append(value); } else { currentStringBuffer.append(varStringBuffer.charAt(index)); } } varStringBuffer.replace(0, varStringBuffer.length(), currentStringBuffer.toString()); } CapoServer.logger.log(Level.FINE, "final replacement = '" + varStringBuffer + "'"); } @SuppressWarnings("rawtypes") public String getVarValue(VariableContainer variableContainer, Enum varName) throws Exception { return getVarValue(variableContainer, varName.toString()); } /** * check request check entries check variables * * @param varName * @return */ public String getVarValue(VariableContainer variableContainer, String varName) throws Exception { if (contextParameterHashMap.containsKey(varName)) { return contextParameterHashMap.get(varName); } else if (delayedParameterHashMap.containsKey(varName)) { return processVars(variableContainer, delayedParameterHashMap.get(varName)); } else if (declaredParameterHashMap.containsKey(varName)) { return declaredParameterHashMap.get(varName); } else if (variableContainer != null && variableContainer.getVarValue(varName) != null) { return variableContainer.getVarValue(varName); } else { return CapoApplication.getVariableValue(varName); } } // END PARAMETER PROCESSING @Override public LifeCycle getLifeCycle() { return this.lifeCycle; } @Override public State getStreamState(StreamType streamType) { if (streamStateHashMap.containsKey(streamType)) { return streamStateHashMap.get(streamType); } else { return State.NONE; } } @Override public State getResourceState() throws Exception { return resourceState; } protected void setResourceState(State state) { this.resourceState = state; } @Deprecated // TODO remove, never used public void setStreamState(StreamType streamType, State state) { streamStateHashMap.put(streamType, state); } public boolean isIterating() { return isIterating; } @Override public String toString() { return ReflectionUtility.processToString(this); } @Override public boolean isSupportedStreamFormat(StreamType streamType, StreamFormat streamFormat) throws Exception { StreamType[] supporedtStreamTypes = getSupportedStreamTypes(); for (StreamType supportedStreamType : supporedtStreamTypes) { if (supportedStreamType == streamType) { StreamFormat[] supportedStreamFormats = getSupportedStreamFormats(supportedStreamType); for (StreamFormat supportedSrteamFormat : supportedStreamFormats) { if (supportedSrteamFormat == streamFormat) { return true; } } } } return false; } @Override public boolean isSupportedStreamType(StreamType streamType) throws Exception { StreamType[] supporedtStreamTypes = getSupportedStreamTypes(); for (StreamType supportedStreamType : supporedtStreamTypes) { if (supportedStreamType == streamType) { return true; } } return false; } @Override public boolean isSupportedAction(Action action) { Action[] supportedActions = getSupportedActions(); return Arrays.asList(supportedActions).contains(action); } @Override public boolean performAction(VariableContainer variableContainer, Action action, ResourceParameter... resourceParameters) throws Exception { if (isSupportedAction(action) == false) { throw new UnsupportedOperationException(); } else { return this.performAction(variableContainer, action, resourceParameters); } } protected abstract Action[] getSupportedActions(); protected InputStream trackInputStream(InputStream inputStream) { openInputStreamVector.add(inputStream); return inputStream; } protected OutputStream trackOutputStream(OutputStream outputStream) { openOutputStreamVector.add(outputStream); return outputStream; } @Override // TODO public InputStream getInputStream(VariableContainer variableContainer, ResourceParameter... resourceParameters) throws Exception { advanceState(State.STEPPING, variableContainer, resourceParameters); StreamFormat streamFormat = getSupportedStreamFormats(StreamType.INPUT)[0]; ByteArrayInputStream byteArrayInputStream = null; if (streamFormat == StreamFormat.XML_BLOCK) { Element dataElement = readXML(variableContainer, resourceParameters); Document tempDocument = CapoApplication.getDocumentBuilder().newDocument(); tempDocument.appendChild(tempDocument.importNode(dataElement, true)); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); XPath.dumpNode(tempDocument.getDocumentElement(), byteArrayOutputStream); // XPath.dumpNode(tempDocument.getDocumentElement(), System.err); byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); } else if (streamFormat == StreamFormat.PROCESS) { byteArrayInputStream = new ByteArrayInputStream(new byte[0]); } else if (streamFormat == StreamFormat.BLOCK) { byteArrayInputStream = new ByteArrayInputStream(readBlock(variableContainer, resourceParameters)); } else { throw new IOException(this.getClass() + "doesn't support getOutputStream()"); } return byteArrayInputStream; } @Override public OutputStream getOutputStream(VariableContainer variableContainer, ResourceParameter... resourceParameters) throws Exception { advanceState(State.OPEN, variableContainer, resourceParameters); StreamFormat streamFormat = getSupportedStreamFormats(StreamType.OUTPUT)[0]; if (outputStreamTranslater != null) { return outputStreamTranslater.getOutputStream(); } if (streamFormat == StreamFormat.XML_BLOCK) { outputStreamTranslater = new OutputStreamTranslater(variableContainer, this, StreamFormat.XML_BLOCK, resourceParameters); return outputStreamTranslater.getOutputStream(); } else if (streamFormat == StreamFormat.BLOCK) { outputStreamTranslater = new OutputStreamTranslater(variableContainer, this, StreamFormat.BLOCK, resourceParameters); return outputStreamTranslater.getOutputStream(); } else if (streamFormat == StreamFormat.PROCESS) { outputStreamTranslater = new OutputStreamTranslater(variableContainer, this, StreamFormat.PROCESS, resourceParameters); return outputStreamTranslater.getOutputStream(); } else { throw new IOException(this.getClass() + "doesn't support getOutputStream()"); } } @Override public CElement readXML(VariableContainer variableContainer, ResourceParameter... resourceParameters) throws Exception { advanceState(State.STEPPING, variableContainer, resourceParameters); CDocument document = (CDocument) CapoApplication.getDocumentBuilder().newDocument(); CElement dataElement = (CElement) document.createElement("Data"); document.appendChild(dataElement); StreamFormat streamFormat = getSupportedStreamFormats(StreamType.INPUT)[0]; if (streamFormat == StreamFormat.BLOCK) { dataElement.setTextContent(new String(readBlock(variableContainer, resourceParameters))); } else if (streamFormat == StreamFormat.PROCESS) { // skip this as we just want to return an empty element I think } else if (streamFormat == StreamFormat.STREAM) { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); StreamUtil.readInputStreamIntoOutputStream(getInputStream(variableContainer, resourceParameters), byteArrayOutputStream); if (getContentMetaData(variableContainer, resourceParameters).getContentFormatType() == ContentFormatType.XML) { dataElement = (CElement) CapoApplication.getDocumentBuilder().parse(new ByteArrayInputStream(byteArrayOutputStream.toByteArray())).getDocumentElement(); } else { dataElement.setTextContent(new String(byteArrayOutputStream.toByteArray())); } } else { throw new IOException(this.getClass() + "doesn't support getOutputStream()"); } return dataElement; } @Override public void writeXML(VariableContainer variableContainer, CElement element, ResourceParameter... resourceParameters) throws Exception { System.out.println("meh"); advanceState(State.OPEN, variableContainer, resourceParameters); StreamFormat streamFormat = getSupportedStreamFormats(StreamType.OUTPUT)[0]; if (streamFormat == StreamFormat.BLOCK) { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); XPath.dumpNode(element, byteArrayOutputStream); writeBlock(variableContainer, byteArrayOutputStream.toByteArray(), resourceParameters); } else if (streamFormat == StreamFormat.PROCESS) { processOutput(variableContainer, resourceParameters); } else if (streamFormat == StreamFormat.STREAM) { XPath.dumpNode(element, getOutputStream(variableContainer, resourceParameters)); } else { throw new IOException(this.getClass() + "doesn't support getOutputStream()"); } } @Override public byte[] readBlock(VariableContainer variableContainer, ResourceParameter... resourceParameters) throws Exception { advanceState(State.STEPPING, variableContainer, resourceParameters); StreamFormat streamFormat = getSupportedStreamFormats(StreamType.INPUT)[0]; if (streamFormat == StreamFormat.XML_BLOCK) { Element dataElement = readXML(variableContainer, resourceParameters); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); XPath.dumpNode(dataElement, byteArrayOutputStream); return byteArrayOutputStream.toByteArray(); } else if (streamFormat == StreamFormat.PROCESS) { return new byte[0]; } else if (streamFormat == StreamFormat.STREAM) { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); StreamUtil.readInputStreamIntoOutputStream(getInputStream(variableContainer, resourceParameters), byteArrayOutputStream); return byteArrayOutputStream.toByteArray(); } else { throw new IOException(this.getClass() + "doesn't support getOutputStream()"); } } @Override public void writeBlock(VariableContainer variableContainer, byte[] block, ResourceParameter... resourceParameters) throws Exception { advanceState(State.OPEN, variableContainer, resourceParameters); StreamFormat streamFormat = getSupportedStreamFormats(StreamType.OUTPUT)[0]; if (streamFormat == StreamFormat.XML_BLOCK) { Document document = CapoApplication.getDocumentBuilder().newDocument(); CElement dataElement = (CElement) document.createElement("Data"); document.appendChild(dataElement); dataElement.setTextContent(new String(block)); writeXML(variableContainer, dataElement, resourceParameters); } else if (streamFormat == StreamFormat.PROCESS) { processOutput(variableContainer, resourceParameters); } else if (streamFormat == StreamFormat.STREAM) { OutputStream outputStream = getOutputStream(variableContainer, resourceParameters); outputStream.write(block); outputStream.close(); } else { throw new IOException(this.getClass() + "doesn't support getOutputStream()"); } } @Override public void processInput(VariableContainer variableContainer, ResourceParameter... resourceParameters) throws Exception { advanceState(State.OPEN, variableContainer, resourceParameters); StreamFormat streamFormat = getSupportedStreamFormats(StreamType.INPUT)[0]; if (streamFormat == StreamFormat.XML_BLOCK) { readXML(variableContainer, resourceParameters); } else if (streamFormat == StreamFormat.BLOCK) { readBlock(variableContainer, resourceParameters); } else if (streamFormat == StreamFormat.STREAM) { InputStream inputStream = getInputStream(variableContainer, resourceParameters); StreamUtil.readInputStreamIntoOutputStream(inputStream, new NullOutputStream()); } else { throw new IOException(this.getClass() + "doesn't support getOutputStream()"); } } @Override public void processOutput(VariableContainer variableContainer, ResourceParameter... resourceParameters) throws Exception { advanceState(State.OPEN, variableContainer, resourceParameters); StreamFormat streamFormat = getSupportedStreamFormats(StreamType.OUTPUT)[0]; if (streamFormat == StreamFormat.XML_BLOCK) { Document document = CapoApplication.getDocumentBuilder().newDocument(); CElement dataElement = (CElement) document.createElement("Data"); writeXML(variableContainer, dataElement, resourceParameters); } else if (streamFormat == StreamFormat.BLOCK) { writeBlock(variableContainer, new byte[0], resourceParameters); } else if (streamFormat == StreamFormat.STREAM) { OutputStream outputStream = getOutputStream(variableContainer, resourceParameters); outputStream.write(new byte[0]); outputStream.close(); } else { throw new IOException(this.getClass() + "doesn't support getOutputStream()"); } } public ResourceDescriptor getParentResourceDescriptor() { return this.parentResourceDescriptor; } @Override public ResourceDescriptor getChildResourceDescriptor(ControlElement callingControlElement, String relativeURI) throws Exception { advanceState(State.OPEN, null); ContentMetaData contentMetaData = getResourceMetaData(null); if (contentMetaData.isContainer() == true) { //getResourceMetaData(null).getContainedResources(); if(childContentMetaDataList == null) { childContentMetaDataList = getResourceMetaData(null).getContainedResources(); } for (ContentMetaData childContentMetaData : childContentMetaDataList) { if (childContentMetaData.getResourceURI().getBaseURI().endsWith(relativeURI)) { if (callingControlElement == null) { if (childResourceDescriptorHashMap.containsKey(childContentMetaData.getResourceURI().getBaseURI())) { ResourceDescriptor childResourceDescriptor = childResourceDescriptorHashMap.get(childContentMetaData.getResourceURI().getBaseURI()); if(childResourceDescriptor.getResourceState() != State.RELEASED) { return childResourceDescriptor; } } ResourceDescriptor childResourceDescriptor = CapoApplication.getDataManager().getResourceDescriptor(callingControlElement, childContentMetaData.getResourceURI().getBaseURI()); if (childResourceDescriptor != null) { childResourceDescriptor.setParentResourceDescriptor(this); //TODO Hopefully this won't bite us in the butt, but keeps the number of calls way down if(childResourceDescriptor instanceof AbstractResourceDescriptor) { ((AbstractResourceDescriptor)childResourceDescriptor).resourceMetaData = childContentMetaData; } } childResourceDescriptorHashMap.put(childContentMetaData.getResourceURI().getBaseURI(), childResourceDescriptor); return childResourceDescriptor; } else { if (childResourceDescriptorHashMap.containsKey(childContentMetaData.getResourceURI().getBaseURI())) { ResourceDescriptor childResourceDescriptor = childResourceDescriptorHashMap.get(childContentMetaData.getResourceURI().getBaseURI()); if(childResourceDescriptor.getResourceState() != State.RELEASED) { return childResourceDescriptor; } } ResourceDescriptor childResourceDescriptor = callingControlElement.getParentGroup().getResourceDescriptor(callingControlElement, childContentMetaData.getResourceURI().getBaseURI()); if (childResourceDescriptor != null) { childResourceDescriptor.setParentResourceDescriptor(this); //TODO Hopefully this won't bite us in the butt, but keeps the number of calls way down if(childResourceDescriptor instanceof AbstractResourceDescriptor) { ((AbstractResourceDescriptor)childResourceDescriptor).resourceMetaData = childContentMetaData; } } childResourceDescriptorHashMap.put(childContentMetaData.getResourceURI().getBaseURI(), childResourceDescriptor); return childResourceDescriptor; } } } // return CapoApplication.getDataManager().getResourceDescriptor(callingControlElement, getResourceURI()+(relativeURI.startsWith("/") ? relativeURI : "/"+relativeURI)); String path = getResourceURI().getBaseURI(); if (path.endsWith("/")) { path = path.substring(0, path.length()-1); } path = path + (relativeURI.startsWith("/") ? relativeURI : "/" + relativeURI); if (callingControlElement == null) { if (childResourceDescriptorHashMap.containsKey(path)) { ResourceDescriptor childResourceDescriptor = childResourceDescriptorHashMap.get(path); if(childResourceDescriptor.getResourceState() != State.RELEASED) { return childResourceDescriptor; } } ResourceDescriptor childResourceDescriptor = CapoApplication.getDataManager().getResourceDescriptor(callingControlElement, path); if (childResourceDescriptor != null) { childResourceDescriptor.setParentResourceDescriptor(this); } childResourceDescriptorHashMap.put(path, childResourceDescriptor); return childResourceDescriptor; } else { if (childResourceDescriptorHashMap.containsKey(path)) { ResourceDescriptor childResourceDescriptor = childResourceDescriptorHashMap.get(path); if(childResourceDescriptor.getResourceState() != State.RELEASED) { return childResourceDescriptor; } } ResourceDescriptor childResourceDescriptor = callingControlElement.getParentGroup().getResourceDescriptor(callingControlElement, path); if (childResourceDescriptor != null) { childResourceDescriptor.setParentResourceDescriptor(this); } childResourceDescriptorHashMap.put(path, childResourceDescriptor); return childResourceDescriptor; } } else { return null; } } @Override public void setParentResourceDescriptor(ResourceDescriptor parentResourceDescriptor) throws Exception { this.parentResourceDescriptor = parentResourceDescriptor; } private class OutputStreamTranslater implements StreamEventListener { private ByteArrayOutputStream byteArrayOutputStream; private StreamEventFilterOutputStream streamEventFilterOutputStream; private StreamFormat streamFormat; private ResourceDescriptor resourceDescriptor; private ResourceParameter[] resourceParameters; private VariableContainer variableContainer; public OutputStreamTranslater(VariableContainer variableContainer, ResourceDescriptor resourceDescriptor, StreamFormat streamFormat, ResourceParameter... resourceParameters) { byteArrayOutputStream = new ByteArrayOutputStream(); streamEventFilterOutputStream = new StreamEventFilterOutputStream(byteArrayOutputStream); streamEventFilterOutputStream.addStreamEventListener(this); this.streamFormat = streamFormat; this.resourceDescriptor = resourceDescriptor; this.resourceParameters = resourceParameters; this.variableContainer = variableContainer; } public OutputStream getOutputStream() { return streamEventFilterOutputStream; } public void close() { byteArrayOutputStream.reset(); streamEventFilterOutputStream.removeStreamEventListener(this); } @Override public void processStreamEvent(StreamEvent streamEvent) throws IOException { if (streamEvent == StreamEvent.CLOSED) { CapoApplication.logger.fine(new String(byteArrayOutputStream.toByteArray())); // don't write empty blocks if (byteArrayOutputStream.size() == 0 && streamFormat != StreamFormat.PROCESS) { return; } try { if (streamFormat == StreamFormat.XML_BLOCK) { Document document = CapoApplication.getDocumentBuilder().parse(new ByteArrayInputStream(byteArrayOutputStream.toByteArray())); resourceDescriptor.writeXML(variableContainer, (CElement) document.getDocumentElement(), resourceParameters); } else if (streamFormat == StreamFormat.BLOCK) { resourceDescriptor.writeBlock(variableContainer, byteArrayOutputStream.toByteArray(), resourceParameters); } else if (streamFormat == StreamFormat.PROCESS) { resourceDescriptor.processOutput(variableContainer, resourceParameters); } else if (streamFormat == StreamFormat.STREAM) { OutputStream outputStream = resourceDescriptor.getOutputStream(variableContainer, resourceParameters); outputStream.write(byteArrayOutputStream.toByteArray()); outputStream.close(); } byteArrayOutputStream.reset(); } catch (Exception exception) { throw new IOException(exception); } } } } }