/*******************************************************************************
 * Copyright (c) 2004, 2005 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
/*


 */
package org.eclipse.jem.internal.proxy.remote;

import java.io.*;
import java.util.*;
import java.util.logging.Level;

import org.eclipse.jem.internal.proxy.common.CommandException;
import org.eclipse.jem.internal.proxy.common.remote.*;
import org.eclipse.jem.internal.proxy.common.remote.Commands.ValueObject;
import org.eclipse.jem.internal.proxy.core.*;
import org.eclipse.jem.internal.proxy.initParser.tree.*;
 
/**
 * The Remote proxy version of Expression.
 * 
 * @since 1.0.0
 */
public class REMExpression extends Expression {

	private IREMExpressionConnection connection;
	private boolean closed;	// Are we closed.
	
	protected Commands.ValueObject workerValue;	// A worker object so that we don't need to keep creating one and throwing it away.
	
	protected Map beanTypeCache;	// Use to cache pending BeanTypes. Used in conjunction with REMStandardBeanTypeFactory.
	protected Map methodsCache;	// Use to cache pending expression method proxies. Used in conjunction with REMProxyConsants.
	protected Map fieldsCache;	// Use to cache pending expression field proxies. Used in conjunction with REMProxyConsants.
	
	/*
	 * This is very special list. It tries to eliminate unneeded traffic. For example a mark immediately followed by an endmark does
	 * not need to be sent. Many expressions can look like: mark, endmark, endtransaction. This is a do nothing and we don't want
	 * to create a connection to just send this. So this list is used to queue up these and remove them too when found as not needed.
	 * 
	 *  However, this is very tricky because all pushToProxy transactions that actually do something MUST call the processPending() method 
	 *  first to make sure any pending transactions are submitted. Because once a real type transaction, such as assignment occurs, any
	 *  pending transaction is a valid transaction, and no longer a do-nothing transaction.
	 *  
	 *  Each transaction type uses a subclass of PendingTransaction to be an entry on the list.
	 *  
	 *  The pendings currently supported are:
	 *  mark/endmark
	 *  try/catch/endtry
	 *  block/endblock
	 *  
	 *  See each individual transaction type to see how it is handled.
	 */
	protected List pendingTransactions;
	
	/**
	 * PendingTransaction entry.
	 * 
	 * @since 1.1.0
	 */
	protected abstract static class PendingTransaction {
		
		/**
		 * The transaction is now being pushed. The implementation should
		 * actually do the push.
		 * 
		 * @param remExpression The REMExpression for this transaction.
		 * 
		 * @since 1.1.0
		 */
		public abstract void pushTransaction(REMExpression remExpression);
	}
	
	/**
	 * @param registry
	 * 
	 * @since 1.0.0
	 */
	public REMExpression(REMProxyFactoryRegistry registry) {
		super(registry);
	}
	
	/**
	 * Return the expression id for this REMExpression. This id is used on the remote vm to
	 * identify who the request is for.
	 * @return
	 * 
	 * @since 1.1.0
	 */
	protected int getREMExpressionID() {
		return this.hashCode();
	}
	/**
	 * Get the pending transactions list.
	 * @return
	 * 
	 * @since 1.1.0
	 */
	protected List getPendingTransactions() {
		if (pendingTransactions == null)
			pendingTransactions = new ArrayList();
		return pendingTransactions;
	}
	
	// Use this flag when debugging to test if errors are due to improper pending processing.
	// If true they will be treated as if not pending and will be executed immediately.
	private static final boolean EXECUTE_PENDING_IMMEDIATELY = false;
	protected void addPendingTransaction(PendingTransaction pending) {
		if (!EXECUTE_PENDING_IMMEDIATELY)
			getPendingTransactions().add(pending);
		else
			pending.pushTransaction(this);
	}

	private boolean sentData;	// Flag to indicate if we have sent anything yet to the remote vm. This is used for the pending optimizations.
	
	/**
	 * Have we sent any data in this transaction yet.
	 * @return
	 * 
	 * @since 1.1.0
	 */
	protected boolean haveSentData() {
		return sentData;
	}
	
	/**
	 * @return Returns the connection.
	 * 
	 * @since 1.1.0
	 */
	protected IREMExpressionConnection getConnection() {
		if (connection == null) {
			if (!sentData)
				getREMBeanProxyFactory().startTransaction();	// This is the first time we send data, so start transaction.
			
			sentData = true;	// If we are getting a transaction, that means we are sending data.
			connection = (IREMExpressionConnection) getREMRegistry().getFreeConnection();
			// This will actually not be stopped until closeproxy. There could be a slight problem if the expression is never closed.
			// But that shouldn't happen. This is to prevent any proxy that was released during the execution but was used by
			// the expression from being released on the remote vm until after the expression is finished.
			try {
				if (workerValue == null)
					workerValue = new Commands.ValueObject();
				if (expressionProcesserController == null) {
					byte trace = !isTraceSet() ? ExpressionCommands.TRACE_DEFAULT : (isTrace() ? ExpressionCommands.TRACE_ON : ExpressionCommands.TRACE_OFF); 
					connection.startExpressionProcessing(getREMExpressionID(), trace);	// It is a new expression.
				} else {
					fillProxy(expressionProcesserController, workerValue);
					connection.resumeExpression(getREMExpressionID(), workerValue);
					expressionProcesserController = null;
				}
			} catch (IOException e) {
				connection.close();
				ProxyPlugin.getPlugin().getLogger().log(e);
				throwIllegalStateException(IO_EXCEPTION_MSG);
			} catch (CommandException e) {
				ProxyPlugin.getPlugin().getLogger().log(e);
				if (!e.isRecoverable()) {
					connection.close();
					connection = null;
				}
				throwIllegalStateException(COMMAND_EXCEPTION_MSG);
			}	
		}
		return connection;
	}

	/**
	 * General IOException occurred msg.
	 */
	protected static final String IO_EXCEPTION_MSG = ProxyRemoteMessages.REMExpression_IOExceptionSeeLog_INFO_; 
	
	protected static final String COMMAND_EXCEPTION_MSG = ProxyRemoteMessages.REMExpression_CommandExceptionSeeLog_INFO_; 
	
	/**
	 * Throw an an illegal state exception if some general error, in particular an I/O or Command Exception
	 * occurred so that callers know there is something wrong.
	 * 
	 * @param msg
	 * @throws IllegalStateException
	 * 
	 * @since 1.0.0
	 */
	protected void throwIllegalStateException(String msg) throws IllegalStateException {
		throw new IllegalStateException(msg);
	}
	
	/**
	 * Return the registry as a REMProxyFactoryRegistry
	 * @return
	 * 
	 * @since 1.0.0
	 */
	protected final REMProxyFactoryRegistry getREMRegistry() {
		return (REMProxyFactoryRegistry) registry;
	}
	
	/**
	 * Return the bean proxy factory as a REMStandardBeanProxyFactory.
	 * @return
	 * 
	 * @since 1.0.0
	 */
	protected final REMStandardBeanProxyFactory getREMBeanProxyFactory() {
		return (REMStandardBeanProxyFactory) beanProxyFactory;
	}

	/**
	 * Process any pending transactions.
	 * <p>
	 * <b>Note: </b>It is required that all non-pending-participating transactions must
	 * call this method first to make sure pending transactions are sent. If this is
	 * not done, there will be errors in the expression.
	 * 
	 * 
	 * @since 1.1.0
	 */
	protected void processPendingTransactions() {
		if (pendingTransactions != null && !pendingTransactions.isEmpty()) {
			try {
				for (int i = 0; i < pendingTransactions.size(); i++) {
					((PendingTransaction) pendingTransactions.get(i)).pushTransaction(this);
				}
			} finally {
				pendingTransactions.clear();
			}
		}
	}
	
	/**
	 * Get the pending entry from top. If top is 1, then get top entry (i.e. last one added), 2 is next one. 
	 * @param fromTop
	 * @return entry requested, or <code>null</code> if no such entry.
	 * 
	 * @since 1.1.0
	 */
	protected PendingTransaction getPendingEntryFromTop(int fromTop) {
		if (pendingTransactions != null && pendingTransactions.size() >= fromTop) {
			return (PendingTransaction) pendingTransactions.get(pendingTransactions.size()-fromTop);
		} else
			return null;
	}
	
	/**
	 * Pop up the top entry from the pending transactions queue.
	 * @param fromTop how many entries to pop from the pending transaction list.
	 * 
	 * 
	 * @since 1.1.0
	 */
	protected void popPendingEntry(int fromTop) {
		if (pendingTransactions != null)
			if (pendingTransactions.size() > fromTop) {
				while(fromTop-- >0)
					pendingTransactions.remove(pendingTransactions.size()-1);
			} else
				pendingTransactions.clear();
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.jem.internal.proxy.core.Expression#pushToProxy(org.eclipse.jem.internal.proxy.core.IProxy)
	 */
	protected void pushToProxy(IProxy proxy) {
		if (proxy == null || proxy.isBeanProxy())
			pushToProxy((IBeanProxy) proxy);
		else
			pushToExpressionProxy((ExpressionProxy) proxy);
	}
	
	private void pushToProxy(IBeanProxy proxy) {
		processPendingTransactions();
		IREMExpressionConnection connection = getConnection();
		try {
			// Format of push proxy command is:
			//	PushExpressionCommand(push to proxy) followed by:
			//		ValueObject containing the rendered proxy.
			connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.PUSH_TO_PROXY_EXPRESSION_VALUE);
			if (proxy == null)
				workerValue.set();
			else
				((IREMBeanProxy) proxy).renderBean(workerValue);
			connection.pushValueObject(workerValue);
		} catch (IOException e) {
			connection.close();
			ProxyPlugin.getPlugin().getLogger().log(e);
			markInvalid(e.getLocalizedMessage());
			throwIllegalStateException(IO_EXCEPTION_MSG);
		} catch (CommandException e) {
			ProxyPlugin.getPlugin().getLogger().log(e);
			markInvalid(e.getLocalizedMessage());
			if (!e.isRecoverable()) {
				connection.close();
				throwIllegalStateException(COMMAND_EXCEPTION_MSG);
			}			
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jem.internal.proxy.core.Expression#closeProxy()
	 */
	protected void closeProxy() {
		if (!closed) {
			try {
				if (connection != null && connection.isConnected()) {
					try {
							connection.stopExpressionProcessing(getREMExpressionID());
					} catch (IOException e) {
						connection.close();
						ProxyPlugin.getPlugin().getLogger().log(e, Level.INFO);
						// Not throwing an illegal state here because we don't care, other than logging and not 
						// returning the connection to the registry that there was an error on close.
					} finally {
						getREMRegistry().returnConnection(connection);
					}
				}
			} finally {
				closed = true;
				if (sentData)
					getREMBeanProxyFactory().stopTransaction();	// Resume proxy releases. We've sent data at least once.
			}
		}
		methodsCache = null;
		fieldsCache = null;
		beanTypeCache = null;
		pendingTransactions = null;
		connection = null;
	}
	
	private static final Object VOIDTYPE = new Object();	// A void type was sent in expression proxy resolution.
	private static final Object NOTRESOLVED = new Object();	// A not resolved type was sent in expression proxy resolution.
	
	/*
	 * Get the sender to use for pulling the expression proxy resolutions.
	 */
	private BeanProxyValueSender getExpressionProxiesSender() {
		return new BeanProxyValueSender(getREMBeanProxyFactory()) {

			/*
			 * (non-Javadoc)
			 * 
			 * @see org.eclipse.jem.internal.proxy.remote.BeanProxyValueSender#sendValue(org.eclipse.jem.internal.proxy.common.remote.Commands.ValueObject)
			 */
			public void sendValue(ValueObject value) {
				if (value.getType() == Commands.FLAG) {
					switch (value.anInt) {
						case ExpressionCommands.EXPRESSIONPROXY_NOTRESOLVED:
							array[index++] = NOTRESOLVED;
							break;
						case ExpressionCommands.EXPRESSIONPROXY_VOIDTYPE:
							array[index++] = VOIDTYPE;
							break;
						default:
							// Shouldn't happen.
							break;
					}
				} else
					super.sendValue(value);
			}
		};
	}
		
	/*
	 * Process the pulled expression proxy resolutions.
	 */
	private void processpulledExpressionProxies(List expressionProxies, BeanProxyValueSender sender) {

		// It is expected that each entry will coorespond to the next non-null expression proxy and will be the bean proxy or one of the special
		// types.
		int len = expressionProxies.size();
		int j = 0;
		Object[] resolveds = sender.getArray();
		for (int i = 0; i < len; i++) {
			ExpressionProxy ep = (ExpressionProxy) expressionProxies.get(i);
			if (ep != null) {
				Object resolved = resolveds[j++];
				if (resolved == NOTRESOLVED)
					fireProxyNotResolved(ep);
				else if (resolved == VOIDTYPE)
					fireProxyVoid(ep);
				else
					fireProxyResolved(ep, (IBeanProxy) resolved);
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.jem.internal.proxy.core.Expression#pullProxyValue(int, java.util.List)
	 */
	protected IBeanProxy pullProxyValue(int proxycount, List expressionProxies) throws ThrowableProxy, NoExpressionValueException {
		if (!haveSentData()) {
			markAllProxiesNotResolved(expressionProxies);
			return null;	// We haven't pushed any commands, so there is nothing to do. Don't create a connection for this.
		}
		// If there are any pending transactions at this point in time, there is no need to send them. They would be do nothings anyway.
		
		boolean processedExpressionProxies = false;
		IREMExpressionConnection lclConnection = getConnection();
		markInTransaction(lclConnection);
		try {
			Commands.ValueObject proxyids = null;
			BeanProxyValueSender sender = null;
			if (proxycount > 0) {
				proxyids = createExpressionProxiesValueObject(proxycount, expressionProxies);
				sender = getExpressionProxiesSender();
			}
			
			lclConnection.pullValue(getREMExpressionID(), proxyids, sender);
			// If we got this far, then if there are proxies, we need to process these too.
			if (proxycount > 0)
				processpulledExpressionProxies(expressionProxies, sender);
			processedExpressionProxies =true;
			lclConnection.getFinalValue(workerValue);	// Get the returned value.
			return getREMBeanProxyFactory().getBeanProxy(workerValue);
		} catch (CommandErrorException e) {
			try {
				if (e.getErrorCode() == ExpressionCommands.EXPRESSION_NOEXPRESSIONVALUE_EXCEPTION) {
					// Need to turn it into a Throwable.
					ThrowableProxy t = null;
					try {
						getREMBeanProxyFactory().getBeanProxy(e.getValue());	// This will cause a throw to occur, but we don't want it going out, we want to capture it.
					} catch (ThrowableProxy e1) {
						t = e1;
					}
					throw new REMNoExpressionValueException(t);
				}
				getREMBeanProxyFactory().processErrorReturn(e);
			} catch (CommandException e1) {
				ProxyPlugin.getPlugin().getLogger().log(e);
				if (!e.isRecoverable()) {
					lclConnection.close();
					throwIllegalStateException(COMMAND_EXCEPTION_MSG);
				}			
			}
		} catch (CommandException e) {
			ProxyPlugin.getPlugin().getLogger().log(e);
			if (!e.isRecoverable()) {
				lclConnection.close();
				throwIllegalStateException(COMMAND_EXCEPTION_MSG);
			}			
		} finally {
			markEndTransaction(lclConnection);
			if (!processedExpressionProxies)
				markAllProxiesNotResolved(expressionProxies);	// We failed before we could process the expression proxies. So mark all as not resolved.
		}
		return null;
	}
	
	/**
	 * This is called by commands that write some data and will be reading data back immediately 
	 * (i.e. pull value and invoke expression). If we are on a callback thread and have the 
	 * used the connection from the callback thread, we need to tell the callback thread that
	 * it is in a transaction. This is needed because while reading data back there are
	 * sometimes calls back to the vm to get beantype data for new classes. This would 
	 * normally be through a new connection so that it doesn't get stuck in the middle of the
	 * data being sent back. But when running on a callback the same connection is used. So it
	 * would stick data in the middle of the return stream of data. To prevent this we need
	 * to tell the callback thread that it is in a transaction during this call so that any
	 * such new connection requests will get a new connection.
	 * <p>
	 * This is not nestable (i.e. the first markEndTransaction will set it false, even if several nested
	 * markInTransactions are called).
	 * <p>
	 * markEndTransaction must be called in ALL cases, such use try/finally.
	 * @param remConnection the connection to see check against and mark in transaction for.
	 * 
	 * 
	 * @since 1.1.0
	 */
	protected void markInTransaction(IREMExpressionConnection remConnection) {
		Thread thread = Thread.currentThread();
		if (thread instanceof REMCallbackThread) {
			// We are in a callback, and the callback connection is our connection, tell the callback that it is in transaction.
			REMCallbackThread callbackThread = (REMCallbackThread) thread;
			if (callbackThread.getConnection() == remConnection) {
				callbackThread.setIntransaction(true);
			}
		}
	}
	
	/**
	 * Mark end of transaction.
	 * @param remConn REMConnection to test and mark not in connection for.
	 * 
	 * @see REMExpression#markInTransaction(IREMExpressionConnection)
	 * @since 1.1.0
	 */
	protected void markEndTransaction(IREMExpressionConnection remConn) {
		Thread thread = Thread.currentThread();
		if (thread instanceof REMCallbackThread) {
			// We are in a callback, and the callback connection is our connection, tell the callback that it is in transaction.
			REMCallbackThread callbackThread = (REMCallbackThread) thread;
			if (callbackThread.getConnection() == remConn) {
				callbackThread.setIntransaction(false);
			}
		}		
	}

	/**
	 * @param expressionProxies
	 * 
	 * @since 1.1.0
	 */
	private Commands.ValueObject createExpressionProxiesValueObject(int actualCount, List expressionProxies) {
		class ExpressionProxyRetriever implements Commands.ValueRetrieve {
			Iterator expressionProxiesItr;
			Commands.ValueObject worker = new Commands.ValueObject();

			public ExpressionProxyRetriever(List expressionProxies) {
				this.expressionProxiesItr = expressionProxies.iterator();
			}

			public Commands.ValueObject nextValue() {
				worker.set(-1);
				while (expressionProxiesItr.hasNext()) {
					Object parm = expressionProxiesItr.next();
					if (parm != null) {
						worker.set(((ExpressionProxy) parm).getProxyID());
						break;
					}
				} 
				return worker;
			}
		};

		workerValue.setArrayIDS(new ExpressionProxyRetriever(expressionProxies), actualCount, Commands.INT);
		return workerValue;
	}

	/*
	 *  (non-Javadoc)
	 * @see org.eclipse.jem.internal.proxy.core.Expression#pushCastToProxy(org.eclipse.jem.internal.proxy.core.IProxyBeanType)
	 */
	protected void pushCastToProxy(IProxyBeanType type) {
		processPendingTransactions();
		IREMExpressionConnection connection = getConnection();
		try {
			// Format of push cast to proxy command is:
			//	PushExpressionCommand(push cast to proxy) followed by:
			connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.CAST_EXPRESSION_VALUE);
			fillProxy(type, workerValue);
			connection.pushValueObject(workerValue);
		} catch (IOException e) {
			connection.close();			
			ProxyPlugin.getPlugin().getLogger().log(e);
			markInvalid(e.getLocalizedMessage());
			throwIllegalStateException(IO_EXCEPTION_MSG);
		} catch (CommandException e) {
			ProxyPlugin.getPlugin().getLogger().log(e);
			markInvalid(e.getLocalizedMessage());
			if (!e.isRecoverable()) {
				connection.close();
				throwIllegalStateException(COMMAND_EXCEPTION_MSG);
			}			
		}
	}

	/**
	 * Push the proxy bean type in the format depending on expression proxy or beantype proxy.
	 * @param type
	 * @throws IOException
	 * 
	 * @since 1.1.0
	 */
	protected void fillProxy(IProxy type, Commands.ValueObject value) throws IOException {
		//		ValueObject containing the rendered bean type proxy if IBeanTypeProxy or int (for expression proxy id) if expression proxy.
		if (type.isBeanProxy()) {
			((IREMBeanProxy) type).renderBean(value);
		} else {
			ExpressionProxy ep = (ExpressionProxy) type;
			value.set(ep.getProxyID());
		}
	}
	
	/*
	 *  (non-Javadoc)
	 * @see org.eclipse.jem.internal.proxy.core.Expression#pushInstanceofToProxy(org.eclipse.jem.internal.proxy.core.IProxyBeanType)
	 */
	protected void pushInstanceofToProxy(IProxyBeanType type) {
		processPendingTransactions();
		IREMExpressionConnection connection = getConnection();
		try {
			// Format of push instanceof to proxy command is:
			//	PushExpressionCommand(push instanceof to proxy) followed by:
			//		ValueObject containing the rendered bean type proxy or the String representing the name of class.
			connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.INSTANCEOF_EXPRESSION_VALUE);
			fillProxy(type, workerValue);
			connection.pushValueObject(workerValue);
		} catch (IOException e) {
			connection.close();
			ProxyPlugin.getPlugin().getLogger().log(e);
			markInvalid(e.getLocalizedMessage());
			throwIllegalStateException(IO_EXCEPTION_MSG);
		} catch (CommandException e) {
			ProxyPlugin.getPlugin().getLogger().log(e);
			markInvalid(e.getLocalizedMessage());
			if (!e.isRecoverable()) {
				connection.close();
				throwIllegalStateException(COMMAND_EXCEPTION_MSG);
			}			
		}
	}

	/*
	 *  (non-Javadoc)
	 * @see org.eclipse.jem.internal.proxy.core.Expression#pushInfixToProxy(org.eclipse.jem.internal.proxy.initParser.tree.InfixOperator, int)
	 */
	protected void pushInfixToProxy(InfixOperator operator, InternalInfixOperandType operandType) {
		processPendingTransactions();
		IREMExpressionConnection connection = getConnection();
		try {
			// Format of push infix to proxy command is:
			//	PushExpressionCommand(push infix to proxy) followed by:
			//		byte: operator
			//		byte: operandType
			connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.INFIX_EXPRESSION_VALUE);
			connection.pushByte((byte) operator.getValue());
			connection.pushByte((byte) operandType.getValue());
		} catch (IOException e) {
			connection.close();
			ProxyPlugin.getPlugin().getLogger().log(e);
			markInvalid(e.getLocalizedMessage());
			throwIllegalStateException(IO_EXCEPTION_MSG);
		}		
	}

	/*
	 *  (non-Javadoc)
	 * @see org.eclipse.jem.internal.proxy.core.Expression#pushPrefixToProxy(org.eclipse.jem.internal.proxy.initParser.tree.PrefixOperator)
	 */
	protected void pushPrefixToProxy(PrefixOperator operator) {
		processPendingTransactions();
		IREMExpressionConnection connection = getConnection();
		try {
			// Format of push prefix to proxy command is:
			//	PushExpressionCommand(push prefix to proxy) followed by:
			//		byte: operator
			connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.PREFIX_EXPRESSION_VALUE);
			connection.pushByte((byte) operator.getValue());
		} catch (IOException e) {
			connection.close();
			ProxyPlugin.getPlugin().getLogger().log(e);
			markInvalid(e.getLocalizedMessage());
			throwIllegalStateException(IO_EXCEPTION_MSG);
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jem.internal.proxy.core.Expression#pushArrayAccessToProxy(int)
	 */
	protected void pushArrayAccessToProxy(int indexCount) {
		processPendingTransactions();
		IREMExpressionConnection connection = getConnection();
		try {
			// Format of push array access to proxy command is:
			//	PushExpressionCommand(push array acces to proxy) followed by:
			//		int: indexCount
			connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.ARRAY_ACCESS_EXPRESSION_VALUE);
			connection.pushInt(indexCount);
		} catch (IOException e) {
			connection.close();
			ProxyPlugin.getPlugin().getLogger().log(e);
			markInvalid(e.getLocalizedMessage());
			throwIllegalStateException(IO_EXCEPTION_MSG);
		}
	}

	/*
	 *  (non-Javadoc)
	 * @see org.eclipse.jem.internal.proxy.core.Expression#pushArrayCreationToProxy(org.eclipse.jem.internal.proxy.core.IProxyBeanType, int)
	 */
	protected void pushArrayCreationToProxy(IProxyBeanType type, int dimensionCount) {
		processPendingTransactions();
		IREMExpressionConnection connection = getConnection();
		try {
			// Format of push array creation to proxy command is:
			//	PushExpressionCommand(push array creation to proxy) followed by:
			//		ValueObject containing the rendered bean type proxy or the expression proxy.
			//		int: dimension count
			connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.ARRAY_CREATION_EXPRESSION_VALUE);
			fillProxy(type, workerValue);
			connection.pushValueObject(workerValue);
			connection.pushInt(dimensionCount);
		} catch (IOException e) {
			connection.close();
			ProxyPlugin.getPlugin().getLogger().log(e);
			markInvalid(e.getLocalizedMessage());
			throwIllegalStateException(IO_EXCEPTION_MSG);
		} catch (CommandException e) {
			ProxyPlugin.getPlugin().getLogger().log(e);
			markInvalid(e.getLocalizedMessage());
			if (!e.isRecoverable()) {
				connection.close();
				throwIllegalStateException(COMMAND_EXCEPTION_MSG);
			}			
		}
	}

	protected void pushArrayInitializerToProxy(IProxyBeanType type, int stripCount, int expressionCount) {
		processPendingTransactions();
		IREMExpressionConnection connection = getConnection();
		try {
			// Format of push array initializer to proxy command is:
			//	PushExpressionCommand(push array initializer to proxy) followed by:
			//		ValueObject containing the rendered bean type proxy or expression proxy.
			//		int: strip count
			//		int: expression count
			connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.ARRAY_INITIALIZER_EXPRESSION_VALUE);
			fillProxy(type, workerValue);
			connection.pushValueObject(workerValue);
			connection.pushInt(stripCount);
			connection.pushInt(expressionCount);
		} catch (IOException e) {
			connection.close();
			ProxyPlugin.getPlugin().getLogger().log(e);
			markInvalid(e.getLocalizedMessage());
			throwIllegalStateException(IO_EXCEPTION_MSG);
		} catch (CommandException e) {
			ProxyPlugin.getPlugin().getLogger().log(e);
			markInvalid(e.getLocalizedMessage());
			if (!e.isRecoverable()) {
				connection.close();
				throwIllegalStateException(COMMAND_EXCEPTION_MSG);
			}			
		}
	}

	/*
	 *  (non-Javadoc)
	 * @see org.eclipse.jem.internal.proxy.core.Expression#pushClassInstanceCreationToProxy(org.eclipse.jem.internal.proxy.core.IProxyBeanType, int)
	 */
	protected void pushClassInstanceCreationToProxy(IProxyBeanType type, int argumentCount) {
		processPendingTransactions();
		IREMExpressionConnection connection = getConnection();
		try {
			// Format of push class instance creation to proxy command is:
			//	PushExpressionCommand(push class instance creation to proxy) followed by:
			//		ValueObject containing the rendered bean type proxy or the expression proxy
			//		int: argument count
			connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.CLASS_INSTANCE_CREATION_EXPRESSION_VALUE);
			fillProxy(type, workerValue);
			connection.pushValueObject(workerValue);
			connection.pushInt(argumentCount);
		} catch (IOException e) {
			connection.close();
			ProxyPlugin.getPlugin().getLogger().log(e);
			markInvalid(e.getLocalizedMessage());
			throwIllegalStateException(IO_EXCEPTION_MSG);
		} catch (CommandException e) {
			ProxyPlugin.getPlugin().getLogger().log(e);
			markInvalid(e.getLocalizedMessage());
			if (!e.isRecoverable()) {
				connection.close();
				throwIllegalStateException(COMMAND_EXCEPTION_MSG);
			}			
		}
	}

	/*
	 *  (non-Javadoc)
	 * @see org.eclipse.jem.internal.proxy.core.Expression#pushTypeReceiverToProxy(org.eclipse.jem.internal.proxy.core.IProxyBeanType)
	 */ 
	protected void pushTypeReceiverToProxy(IProxyBeanType type) {
		processPendingTransactions();
		IREMExpressionConnection connection = getConnection();
		try {
			// Format of push type receiver to proxy command is:
			//	PushExpressionCommand(push type receiver to proxy) followed by:
			//		ValueObject containing the rendered bean type proxy or the expression proxy.
			connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.TYPERECEIVER_EXPRESSION_VALUE);
			fillProxy(type, workerValue);
			connection.pushValueObject(workerValue);
		} catch (IOException e) {
			connection.close();
			ProxyPlugin.getPlugin().getLogger().log(e);
			markInvalid(e.getLocalizedMessage());
			throwIllegalStateException(IO_EXCEPTION_MSG);
		} catch (CommandException e) {
			ProxyPlugin.getPlugin().getLogger().log(e);
			markInvalid(e.getLocalizedMessage());
			if (!e.isRecoverable()) {
				connection.close();
				throwIllegalStateException(COMMAND_EXCEPTION_MSG);
			}			
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jem.internal.proxy.core.Expression#pushFieldAccessToProxy(java.lang.String, boolean)
	 */
	protected void pushFieldAccessToProxy(Object field, boolean hasReceiver) {
		processPendingTransactions();
		IREMExpressionConnection connection = getConnection();
		try {
			// Format of push field access to proxy command is:
			//	PushExpressionCommand(push field access to proxy) followed by:
			//		Commands.Value: fieldName or IProxyField
			//		boolean: hasReceiver
			connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.FIELD_ACCESS_EXPRESSION_VALUE);
			if (field instanceof String) {
				workerValue.set((String) field);
			} else {
				fillProxy((IProxy) field, workerValue);
			}
			connection.pushValueObject(workerValue);
			connection.pushBoolean(hasReceiver);
		} catch (IOException e) {
			connection.close();
			ProxyPlugin.getPlugin().getLogger().log(e);
			markInvalid(e.getLocalizedMessage());
			throwIllegalStateException(IO_EXCEPTION_MSG);
		} catch (CommandException e) {
			ProxyPlugin.getPlugin().getLogger().log(e);
			markInvalid(e.getLocalizedMessage());
			if (!e.isRecoverable()) {
				connection.close();
				throwIllegalStateException(COMMAND_EXCEPTION_MSG);
			}			
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jem.internal.proxy.core.Expression#pushMethodInvocationToProxy(java.lang.String, boolean, int)
	 */
	protected void pushMethodInvocationToProxy(Object method, boolean hasReceiver, int argCount) {
		processPendingTransactions();
		IREMExpressionConnection connection = getConnection();
		try {
			// Format of push method invocation to proxy command is:
			//	PushExpressionCommand(push method invocation to proxy) followed by:
			//		Commands.ValueObject: methodName or IMethodProxy
			//		boolean: hasReceiver
			//		int: argCount
			connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.METHOD_EXPRESSION_VALUE);
			if (method instanceof String) {
				workerValue.set((String) method);
			} else {
				fillProxy((IProxy) method, workerValue);
			}
			connection.pushValueObject(workerValue);
			connection.pushBoolean(hasReceiver);
			connection.pushInt(argCount);
		} catch (IOException e) {
			connection.close();
			ProxyPlugin.getPlugin().getLogger().log(e);
			markInvalid(e.getLocalizedMessage());
			throwIllegalStateException(IO_EXCEPTION_MSG);
		} catch (CommandException e) {
			ProxyPlugin.getPlugin().getLogger().log(e);
			markInvalid(e.getLocalizedMessage());
			if (!e.isRecoverable()) {
				connection.close();
				throwIllegalStateException(COMMAND_EXCEPTION_MSG);
			}			
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jem.internal.proxy.core.Expression#pushConditionalToProxy(int)
	 */
	protected void pushConditionalToProxy(InternalConditionalOperandType expressionType) {
		processPendingTransactions();
		IREMExpressionConnection connection = getConnection();
		try {
			// Format of push conditional to proxy command is:
			//	PushExpressionCommand(push conditional to proxy) followed by:
			//		byte: expression type
			connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.CONDITIONAL_EXPRESSION_VALUE);
			connection.pushByte((byte) expressionType.getValue());
		} catch (IOException e) {
			connection.close();
			ProxyPlugin.getPlugin().getLogger().log(e);
			markInvalid(e.getLocalizedMessage());
			throwIllegalStateException(IO_EXCEPTION_MSG);
		}
	}

	/*
	 * A special one that takes the ThrowableProxy for no expression value and 
	 * wrappers it prints its stack trace instead, but still makes it a subclass
	 * of NoExpressionValueException.
	 * 
	 * @since 1.1.0
	 */
	private static class REMNoExpressionValueException extends NoExpressionValueException {
		/**
		 * Comment for <code>serialVersionUID</code>
		 * 
		 * @since 1.1.0
		 */
		private static final long serialVersionUID = 1692406777391812694L;


		public REMNoExpressionValueException(ThrowableProxy e) {
			super(e);
		}
		
		
		/* (non-Javadoc)
		 * @see java.lang.Throwable#getLocalizedMessage()
		 */
		public String getLocalizedMessage() {
			return ((ThrowableProxy) getCause()).getProxyLocalizedMessage();
		}
		
		
		/* (non-Javadoc)
		 * @see java.lang.Throwable#getMessage()
		 */
		public String getMessage() {
			return ((ThrowableProxy) getCause()).getProxyMessage();
		}
		
		
		/* (non-Javadoc)
		 * @see java.lang.Throwable#printStackTrace()
		 */
		public void printStackTrace() {
			getCause().printStackTrace();
		}
		
		
		/* (non-Javadoc)
		 * @see java.lang.Throwable#printStackTrace(java.io.PrintStream)
		 */
		public void printStackTrace(PrintStream s) {
			getCause().printStackTrace(s);
		}
		
		
		/* (non-Javadoc)
		 * @see java.lang.Throwable#printStackTrace(java.io.PrintWriter)
		 */
		public void printStackTrace(PrintWriter s) {
			getCause().printStackTrace(s);
		}
	}
	
	/*
	 *  (non-Javadoc)
	 * @see org.eclipse.jem.internal.proxy.core.Expression#pushInvoke(int, java.util.List)
	 */
	protected void pushInvoke(int proxycount, List expressionProxies) throws ThrowableProxy, NoExpressionValueException {
		if (!haveSentData()) {
			markAllProxiesNotResolved(expressionProxies);
			return;	// We haven't pushed any commands, so there is nothing to do. Don't create a connection for this.
		}
		// If at this point there are pending transactions, there is no need to send them because they would all be do-nothings.
		
		boolean processedExpressionProxies = false;
		IREMExpressionConnection lclConnection = getConnection();
		markInTransaction(lclConnection);
		try {
			Commands.ValueObject proxyids = null;
			BeanProxyValueSender sender = null;
			if (proxycount > 0) {
				proxyids = createExpressionProxiesValueObject(proxycount, expressionProxies);
				sender = getExpressionProxiesSender();
			}

			lclConnection.sync(getREMExpressionID(), proxyids, sender);
			
			// If we got this far, then if there are proxies, we need to process these too.
			if (proxycount > 0)
				processpulledExpressionProxies(expressionProxies, sender);
			processedExpressionProxies = true;
			lclConnection.getFinalValue(workerValue);	// We don't care what it is, we just need to see if there is an error.
		} catch (CommandErrorException e) {
			try {
				if (e.getErrorCode() == ExpressionCommands.EXPRESSION_NOEXPRESSIONVALUE_EXCEPTION) {
					// Need to turn it into a Throwable.
					ThrowableProxy t = null;
					try {
						getREMBeanProxyFactory().getBeanProxy(e.getValue());	// This will cause a throw to occur, but we don't want it going out, we want to capture it.
					} catch (ThrowableProxy e1) {
						t = e1;
					}
					throw new REMNoExpressionValueException(t);
				}
				getREMBeanProxyFactory().processErrorReturn(e);
			} catch (CommandException e1) {
				ProxyPlugin.getPlugin().getLogger().log(e);
				if (!e.isRecoverable()) {
					lclConnection.close();
					throwIllegalStateException(COMMAND_EXCEPTION_MSG);
				}			
			}
		} catch (CommandException e) {
			ProxyPlugin.getPlugin().getLogger().log(e);
			if (!e.isRecoverable()) {
				lclConnection.close();
				throwIllegalStateException(COMMAND_EXCEPTION_MSG);
			}			
		} finally {
			markEndTransaction(lclConnection);
			if (!processedExpressionProxies)
				markAllProxiesNotResolved(expressionProxies);	// We failed before we could process the expression proxies. So mark all as not resolved.
		}
	}

	private static class REMBeanTypeExpressionProxy extends ExpressionProxy implements IBeanTypeExpressionProxy {
		
		private String typeName;
		
		/**
		 * @param proxyid
		 * 
		 * @since 1.1.0
		 */
		private REMBeanTypeExpressionProxy(int proxyid, Expression expression) {
			super(proxyid, BEANTYPE_EXPRESSION_PROXY, expression);
		}
		
		public void setTypeName(String typeName) {
			this.typeName = typeName;
		}
		
		public String getTypeName() {
			return typeName;
		}
		
		
		/* (non-Javadoc)
		 * @see org.eclipse.jem.internal.proxy.core.ExpressionProxy#toString()
		 */
		public String toString() {
			return super.toString()+" - "+getTypeName(); //$NON-NLS-1$
		}
		
		
		/* (non-Javadoc)
		 * @see org.eclipse.jem.internal.proxy.core.IProxyBeanType#getMethodProxy(org.eclipse.jem.internal.proxy.core.IExpression, java.lang.String, org.eclipse.jem.internal.proxy.core.IProxyBeanType[])
		 */
		public IProxyMethod getMethodProxy(IExpression expression, String methodName, IProxyBeanType[] parameterTypes) {
			REMProxyFactoryRegistry registry = (REMProxyFactoryRegistry) expression.getRegistry();
			return ((REMMethodProxyFactory) registry.getMethodProxyFactory()).getMethodProxy(expression, this, methodName, parameterTypes);
		}
		
		
		/* (non-Javadoc)
		 * @see org.eclipse.jem.internal.proxy.core.IProxyBeanType#getMethodProxy(org.eclipse.jem.internal.proxy.core.IExpression, java.lang.String, java.lang.String[])
		 */
		public IProxyMethod getMethodProxy(IExpression expression, String methodName, String[] parameterTypes) {
			REMProxyFactoryRegistry registry = (REMProxyFactoryRegistry) expression.getRegistry();
			return ((REMMethodProxyFactory) registry.getMethodProxyFactory()).getMethodProxy(expression, this, methodName, parameterTypes);
		}
		
		public IProxyMethod getMethodProxy(IExpression expression, String methodName) {
			return getMethodProxy(expression, methodName, (IProxyBeanType[]) null);
		}

		/* (non-Javadoc)
		 * @see org.eclipse.jem.internal.proxy.core.IProxyBeanType#getFieldProxy(org.eclipse.jem.internal.proxy.core.IExpression, java.lang.String)
		 */
		public IProxyField getFieldProxy(IExpression expression, String fieldName) {
			REMProxyFactoryRegistry registry = (REMProxyFactoryRegistry) expression.getRegistry();
			return ((REMMethodProxyFactory) registry.getMethodProxyFactory()).getFieldProxy(expression, this, fieldName);
		}
	}
	
	private static class REMMethodExpressionProxy extends ExpressionProxy implements IProxyMethod {
		
			/**
		 * @param proxyid
		 * @param proxyType
		 * @param expression
		 * 
		 * @since 1.1.0
		 */
		private REMMethodExpressionProxy(int proxyid, Expression expression) {
			super(proxyid, METHOD_EXPRESSION_PROXY, expression);
		}
	}
	
	private static class REMFieldExpressionProxy extends ExpressionProxy implements IProxyField {
		
			/**
		 * @param proxyid
		 * @param proxyType
		 * @param expression
		 * 
		 * @since 1.1.0
		 */
		private REMFieldExpressionProxy(int proxyid, Expression expression) {
			super(proxyid, FIELD_EXPRESSION_PROXY, expression);
		}
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.jem.internal.proxy.core.Expression#createExpressionProxy(int)
	 */
	protected ExpressionProxy createExpressionProxy(int proxyType, int proxyID) {
		switch (proxyType) {
			case NORMAL_EXPRESSION_PROXY:
			default:
				return new ExpressionProxy(proxyID, NORMAL_EXPRESSION_PROXY, this);
			
			case BEANTYPE_EXPRESSION_PROXY:
				return new REMBeanTypeExpressionProxy(proxyID, this);
				
			case METHOD_EXPRESSION_PROXY:
				return new REMMethodExpressionProxy(proxyID, this);
				
			case FIELD_EXPRESSION_PROXY:
				return new REMFieldExpressionProxy(proxyID, this);
		}
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.jem.internal.proxy.core.Expression#pushAssignmentToProxy(org.eclipse.jem.internal.proxy.core.ExpressionProxy)
	 */
	protected void pushAssignmentToProxy(ExpressionProxy proxy) {
		processPendingTransactions();
		IREMExpressionConnection connection = getConnection();
		try {
			// Format of push assignment to proxy command is:
			//	PushExpressionCommand(push assignment to proxy) followed by:
			//		int: proxy id
			connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.ASSIGNMENT_PROXY_EXPRESSION_VALUE);
			connection.pushInt(proxy.getProxyID());
		} catch (IOException e) {
			connection.close();
			ProxyPlugin.getPlugin().getLogger().log(e);
			markInvalid(e.getLocalizedMessage());
			throwIllegalStateException(IO_EXCEPTION_MSG);
		}
	}
	
	
	/* (non-Javadoc)
	 * @see org.eclipse.jem.internal.proxy.core.Expression#pushAssignmentToProxy()
	 */
	protected void pushAssignmentToProxy() {
		processPendingTransactions();
		IREMExpressionConnection connection = getConnection();
		try {
			// Format of the push assignment command is:
			//   PushAssignmentCommand.
			connection.pushExpressionCommand(getREMExpressionID(), (byte) InternalExpressionTypes.ASSIGNMENT_EXPRESSION_VALUE);
		} catch (IOException e) {
			connection.close();
			ProxyPlugin.getPlugin().getLogger().log(e);
			markInvalid(e.getLocalizedMessage());
			throwIllegalStateException(IO_EXCEPTION_MSG);
		}
	}
	

	private void pushToExpressionProxy(ExpressionProxy proxy) {
		processPendingTransactions();
		IREMExpressionConnection connection = getConnection();
		try {
			// Format of push to expression proxy command is:
			//	PushExpressionCommand(push expression proxy to proxy) followed by:
			//		int: proxy id
			connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.PUSH_TO_EXPRESSION_PROXY_EXPRESSION_VALUE);
			connection.pushInt(proxy.getProxyID());
		} catch (IOException e) {
			connection.close();
			ProxyPlugin.getPlugin().getLogger().log(e);
			markInvalid(e.getLocalizedMessage());
			throwIllegalStateException(IO_EXCEPTION_MSG);
		}

	}
	
	private static class BlockBegin extends PendingTransaction {
		public int blockNumber;
		
		public BlockBegin(int blockNumber) {
			this.blockNumber = blockNumber;
		}
		
		public void pushTransaction(REMExpression remExpression) {
			IREMExpressionConnection connection = remExpression.getConnection();
			try {
				// Format of push to block begin proxy command is:
				//	PushExpressionCommand(push block begin proxy to proxy) followed by:
				//		int: block id
				connection.pushExpressionCommand(remExpression.getREMExpressionID(), (byte)InternalExpressionTypes.BLOCK_BEGIN_EXPRESSION_VALUE);
				connection.pushInt(blockNumber);
			} catch (IOException e) {
				connection.close();
				ProxyPlugin.getPlugin().getLogger().log(e);
				remExpression.markInvalid(e.getLocalizedMessage());
				remExpression.throwIllegalStateException(IO_EXCEPTION_MSG);
			}			
		}
		
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.jem.internal.proxy.core.Expression#pushBlockBeginToProxy(int)
	 */
	protected void pushBlockBeginToProxy(int blockNumber) {
		addPendingTransaction(new BlockBegin(blockNumber));
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.jem.internal.proxy.core.Expression#pushBlockEndToProxy(int)
	 */
	protected void pushBlockEndToProxy(int blockNumber) {
		// See if the top pending transactions is BreakBlock(blockNumber). If it is then the BreakBlock can be thrown away.
		PendingTransaction topEntry = getPendingEntryFromTop(1);
		if (topEntry instanceof BlockBreak && ((BlockBreak) topEntry).blockNumber == blockNumber) {
			popPendingEntry(1);
			topEntry = getPendingEntryFromTop(1);
		}
		// See if the top pending transaction is now BeginBlock(blockNumber). If it is, then this transaction and the block begin
		// can be thrown away because they are an empty block.
		if (topEntry instanceof BlockBegin && ((BlockBegin) topEntry).blockNumber == blockNumber) {
			popPendingEntry(1);
			return;
		}
		processPendingTransactions();
		IREMExpressionConnection connection = getConnection();
		try {
			// Format of push to block end proxy command is:
			//	PushExpressionCommand(push block end proxy to proxy) followed by:
			//		int: block id
			connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.BLOCK_END_EXPRESSION_VALUE);
			connection.pushInt(blockNumber);
		} catch (IOException e) {
			connection.close();
			ProxyPlugin.getPlugin().getLogger().log(e);
			markInvalid(e.getLocalizedMessage());
			throwIllegalStateException(IO_EXCEPTION_MSG);
		}
	}
	
	private static class BlockBreak extends PendingTransaction {
		public int blockNumber;
		
		public BlockBreak(int blockNumber) {
			this.blockNumber = blockNumber;
		}
		
		public void pushTransaction(REMExpression remExpression) {
			IREMExpressionConnection connection = remExpression.getConnection();
			try {
				// Format of push to block break proxy command is:
				//	PushExpressionCommand(push block break proxy to proxy) followed by:
				//		int: block id
				connection.pushExpressionCommand(remExpression.getREMExpressionID(), (byte)InternalExpressionTypes.BLOCK_BREAK_EXPRESSION_VALUE);
				connection.pushInt(blockNumber);
			} catch (IOException e) {
				connection.close();
				ProxyPlugin.getPlugin().getLogger().log(e);
				remExpression.markInvalid(e.getLocalizedMessage());
				remExpression.throwIllegalStateException(IO_EXCEPTION_MSG);
			}
		}		
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jem.internal.proxy.core.Expression#pushBlockBreakToProxy(int)
	 */
	protected void pushBlockBreakToProxy(int blockNumber) {
		// Even if there is no pending block begin for this block, we will pend the break.
		// This is so that if the break occurred just before the block end, then it can be ignored.
		addPendingTransaction(new BlockBreak(blockNumber));
	}

	private static class TryBegin extends PendingTransaction {

		public final int tryNumber;
		
		public TryBegin(int tryNumber) {
			this.tryNumber = tryNumber;
			
		}
		public void pushTransaction(REMExpression remExpression) {
			IREMExpressionConnection connection = remExpression.getConnection();
			try {
				// Format of push to try begin proxy command is:
				//	PushExpressionCommand(push try begin to proxy) followed by:
				//		int: try id
				connection.pushExpressionCommand(remExpression.getREMExpressionID(), (byte)InternalExpressionTypes.TRY_BEGIN_EXPRESSION_VALUE);
				connection.pushInt(tryNumber);
			} catch (IOException e) {
				connection.close();
				ProxyPlugin.getPlugin().getLogger().log(e);
				remExpression.markInvalid(e.getLocalizedMessage());
				remExpression.throwIllegalStateException(IO_EXCEPTION_MSG);
			}
		}
		
	}
	/* (non-Javadoc)
	 * @see org.eclipse.jem.internal.proxy.core.Expression#pushTryBeginToProxy(int)
	 */
	protected void pushTryBeginToProxy(int tryNumber) {
		addPendingTransaction(new TryBegin(tryNumber));
	}

	private static class TryCatch extends PendingTransaction {

		public final int tryNumber;
		private final IProxyBeanType exceptionType;
		private final ExpressionProxy ep;

		public TryCatch(int tryNumber, IProxyBeanType exceptionType, ExpressionProxy ep) {
			this.tryNumber = tryNumber;
			this.exceptionType = exceptionType;
			this.ep = ep;
		}
		
		public void pushTransaction(REMExpression remExpression) {
			IREMExpressionConnection connection = remExpression.getConnection();
			try {
				// Format of push to try begin proxy command is:
				//	PushExpressionCommand(push try begin to proxy) followed by:
				//		int: try id
				//		object: expression type (as beantype or as expression proxy)
				//		int: proxy id or (-1 if null).
				connection.pushExpressionCommand(remExpression.getREMExpressionID(), (byte)InternalExpressionTypes.TRY_CATCH_EXPRESSION_VALUE);
				connection.pushInt(tryNumber);
				remExpression.fillProxy(exceptionType, remExpression.workerValue);
				connection.pushValueObject(remExpression.workerValue);
				if (ep != null)
					connection.pushInt(ep.getProxyID());
				else
					connection.pushInt(-1);
			} catch (IOException e) {
				connection.close();
				ProxyPlugin.getPlugin().getLogger().log(e);
				remExpression.markInvalid(e.getLocalizedMessage());
				remExpression.throwIllegalStateException(IO_EXCEPTION_MSG);
			} catch (CommandException e) {
				ProxyPlugin.getPlugin().getLogger().log(e);
				remExpression.markInvalid(e.getLocalizedMessage());
				if (!e.isRecoverable()) {
					connection.close();
					remExpression.throwIllegalStateException(COMMAND_EXCEPTION_MSG);
				}			
			}
		}
		
	}
	/*
	 *  (non-Javadoc)
	 * @see org.eclipse.jem.internal.proxy.core.Expression#pushTryCatchClauseToProxy(int, org.eclipse.jem.internal.proxy.core.IProxyBeanType, org.eclipse.jem.internal.proxy.core.ExpressionProxy)
	 */
	protected void pushTryCatchClauseToProxy(int tryNumber, IProxyBeanType exceptionType, ExpressionProxy ep) {
		addPendingTransaction(new TryCatch(tryNumber, exceptionType, ep));
	}

	private static class TryFinally extends PendingTransaction {
		
		public final int tryNumber;

		public TryFinally(int tryNumber) {
			this.tryNumber = tryNumber;
			
		}

		public void pushTransaction(REMExpression remExpression) {
			IREMExpressionConnection connection = remExpression.getConnection();
			try {
				// Format of push to try begin proxy command is:
				//	PushExpressionCommand(push try finally to proxy) followed by:
				//		int: try id
				connection.pushExpressionCommand(remExpression.getREMExpressionID(), (byte)InternalExpressionTypes.TRY_FINALLY_EXPRESSION_VALUE);
				connection.pushInt(tryNumber);
			} catch (IOException e) {
				connection.close();
				ProxyPlugin.getPlugin().getLogger().log(e);
				remExpression.markInvalid(e.getLocalizedMessage());
				remExpression.throwIllegalStateException(IO_EXCEPTION_MSG);
			}
		}
		
	}
	/* (non-Javadoc)
	 * @see org.eclipse.jem.internal.proxy.core.Expression#pushTryFinallyClauseToProxy(int)
	 */
	protected void pushTryFinallyClauseToProxy(int tryNumber) {
		addPendingTransaction(new TryFinally(tryNumber));
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jem.internal.proxy.core.Expression#pushTryEndToProxy(int)
	 */
	protected void pushTryEndToProxy(int tryNumber) {
		// This is a little tricky. We need to find if there is nothing but try/catch/finally for this tryNumber on the pending
		// transactions up to the try begin, if there is nothing else, then we can throw the entire try away. That
		// means there was no code at all in any of the try/catch/finally blocks.
		int fromTop = 0;
		while (true) {
			PendingTransaction topEntry = getPendingEntryFromTop(++fromTop);
			if (topEntry instanceof TryFinally) {
				if (((TryFinally) topEntry).tryNumber != tryNumber)
					break;	// We met a finally that wasn't ours, so entire try group must be sent.
			} else if (topEntry instanceof TryCatch) {
				if (((TryCatch) topEntry).tryNumber != tryNumber)
					break;	// We met a catch that wasn't ours, so entire try group must be sent.
			} else if (topEntry instanceof TryBegin) {
				if (((TryBegin) topEntry).tryNumber == tryNumber) {
					// We've met our try begin, and nothing but empty catch/finally in between, so the entire group can be thrown away
					popPendingEntry(fromTop);
					return;
				} else
					break;	// We've hit a try begin that wasn't ours, so the entire try group must be sent.	
			} else
				break;	// We've hit something other than our try group, so process everything.
		}
		processPendingTransactions();
		IREMExpressionConnection connection = getConnection();
		try {
			// Format of push to try begin proxy command is:
			//	PushExpressionCommand(push try end to proxy) followed by:
			//		int: try id
			connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.TRY_END_EXPRESSION_VALUE);
			connection.pushInt(tryNumber);
		} catch (IOException e) {
			connection.close();
			ProxyPlugin.getPlugin().getLogger().log(e);
			markInvalid(e.getLocalizedMessage());
			throwIllegalStateException(IO_EXCEPTION_MSG);
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jem.internal.proxy.core.Expression#pushThrowToProxy()
	 */
	protected void pushThrowToProxy() {
		processPendingTransactions();
		IREMExpressionConnection connection = getConnection();
		try {
			// Format of push to try begin proxy command is:
			//	PushExpressionCommand(push throw to proxy)
			connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.THROW_EXPRESSION_VALUE);
		} catch (IOException e) {
			connection.close();
			ProxyPlugin.getPlugin().getLogger().log(e);
			markInvalid(e.getLocalizedMessage());
			throwIllegalStateException(IO_EXCEPTION_MSG);
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jem.internal.proxy.core.Expression#pushRethrowToProxy(int)
	 */
	protected void pushRethrowToProxy(int tryNumber) {
		processPendingTransactions();
		IREMExpressionConnection connection = getConnection();
		try {
			// Format of push to rethow proxy command is:
			//	PushExpressionCommand(push rethrow to proxy)
			//		int: try id
			connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.RETHROW_EXPRESSION_VALUE);
			connection.pushInt(tryNumber);
		} catch (IOException e) {
			connection.close();
			ProxyPlugin.getPlugin().getLogger().log(e);
			markInvalid(e.getLocalizedMessage());
			throwIllegalStateException(IO_EXCEPTION_MSG);
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jem.internal.proxy.core.Expression#pushBeanTypeToProxy(org.eclipse.jem.internal.proxy.core.IBeanTypeExpressionProxy)
	 */
	protected void pushBeanTypeToProxy(IBeanTypeExpressionProxy proxy) {
		// Push beantype to proxy is sent out of sequence without respect to where in expression we are,
		// so no need to handle pending transactions at this point. They would not affect the result
		// of this call.
		IREMExpressionConnection connection = getConnection();
		try {
			// Format of push to beanType proxy command is:
			//	PushExpressionCommand(push bean type expression proxy)
			//		int: proxy id
			//		string: typename
			connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.PUSH_BEANTYPE_EXPRESSIONPROXY_EXPRESSION_VALUE);
			REMBeanTypeExpressionProxy ep = (REMBeanTypeExpressionProxy) proxy;
			connection.pushInt(ep.getProxyID());
			connection.pushString(ep.getTypeName());
		} catch (IOException e) {
			connection.close();
			ProxyPlugin.getPlugin().getLogger().log(e);
			markInvalid(e.getLocalizedMessage());
			throwIllegalStateException(IO_EXCEPTION_MSG);
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jem.internal.proxy.core.Expression#pushMethodToProxy(org.eclipse.jem.internal.proxy.core.ExpressionProxy, org.eclipse.jem.internal.proxy.core.IProxyBeanType, java.lang.String, org.eclipse.jem.internal.proxy.core.IProxyBeanType[])
	 */
	protected void pushMethodToProxy(ExpressionProxy proxy, IProxyBeanType declaringType, String methodName, IProxyBeanType[] parameterTypes) {
		// Push method to proxy is sent out of sequence without respect to where in expression we are,
		// so no need to handle pending transactions at this point. They would not affect the result
		// of this call.

		IREMExpressionConnection connection = getConnection();
		try {
			// Format of push to method proxy command is:
			//	PushExpressionCommand(push method type expression proxy)
			//		int: proxy id
			//		ValueObject: containing the rendered bean type proxy or the expression proxy for the declaring type
			//		string: method name
			//		int: number of parameter types
			//		ValueObject(s): containing the rendered bean type proxy or the expression proxy for the parameter types.
			connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.PUSH_METHOD_EXPRESSIONPROXY_EXPRESSION_VALUE);
			connection.pushInt(proxy.getProxyID());
			fillProxy(declaringType, workerValue);
			connection.pushValueObject(workerValue);
			connection.pushString(methodName);
			if (parameterTypes == null || parameterTypes.length == 0)
				connection.pushInt(0);
			else {
				connection.pushInt(parameterTypes.length);
				for (int i = 0; i < parameterTypes.length; i++) {
					fillProxy(parameterTypes[i], workerValue);
					connection.pushValueObject(workerValue);					
				}
			}
		} catch (IOException e) {
			connection.close();
			ProxyPlugin.getPlugin().getLogger().log(e);
			markInvalid(e.getLocalizedMessage());
			throwIllegalStateException(IO_EXCEPTION_MSG);
		} catch (CommandException e) {
			ProxyPlugin.getPlugin().getLogger().log(e);
			markInvalid(e.getLocalizedMessage());
			if (!e.isRecoverable()) {
				connection.close();
				throwIllegalStateException(COMMAND_EXCEPTION_MSG);
			}			
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jem.internal.proxy.core.Expression#pushFieldToProxy(org.eclipse.jem.internal.proxy.core.ExpressionProxy, org.eclipse.jem.internal.proxy.core.IProxyBeanType, java.lang.String)
	 */
	protected void pushFieldToProxy(ExpressionProxy proxy, IProxyBeanType declaringType, String fieldName) {
		// Push field to proxy is sent out of sequence without respect to where in expression we are,
		// so no need to handle pending transactions at this point. They would not affect the result
		// of this call.

		IREMExpressionConnection connection = getConnection();
		try {
			// Format of push to field proxy command is:
			//	PushExpressionCommand(push field type expression proxy)
			//		int: proxy id
			//		ValueObject: containing the rendered bean type proxy or the expression proxy for the declaring type
			//		string: field name
			connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.PUSH_FIELD_EXPRESSIONPROXY_EXPRESSION_VALUE);
			connection.pushInt(proxy.getProxyID());
			fillProxy(declaringType, workerValue);
			connection.pushValueObject(workerValue);
			connection.pushString(fieldName);
		} catch (IOException e) {
			connection.close();
			ProxyPlugin.getPlugin().getLogger().log(e);
			markInvalid(e.getLocalizedMessage());
			throwIllegalStateException(IO_EXCEPTION_MSG);
		} catch (CommandException e) {
			ProxyPlugin.getPlugin().getLogger().log(e);
			markInvalid(e.getLocalizedMessage());
			if (!e.isRecoverable()) {
				connection.close();
				throwIllegalStateException(COMMAND_EXCEPTION_MSG);
			}			
		}
	}
	
	/**
	 * Get the map of IProxyMethods for a beantype. Meant to be used only in conjunction with REMProxyConstants.
	 * It is here so the REMProxyConstants can store pending proxies per expression.
	 * 
	 * @param beanType
	 * @return
	 * 
	 * @since 1.1.0
	 */
	public Map getMethods(IProxyBeanType beanType) {
		if (methodsCache == null)
			methodsCache = new HashMap();
		Map methods = (Map) methodsCache.get(beanType.getTypeName());
		if(methods == null){
			methods = new HashMap(20);
			methodsCache.put(beanType.getTypeName(),methods);
		}
		return methods;	
	}
	
	/**
	 * Get the map of IProxyFields for a beantype. Meant to be used only in conjunction with REMProxyConstants.
	 * It is here so the REMProxyConstants can store pending proxies per expression.
	 * 
	 * @param beanType
	 * @return
	 * 
	 * @since 1.1.0
	 */
	public Map getFields(IProxyBeanType beanType) {
		if (fieldsCache == null)
			fieldsCache = new HashMap();
		Map fields = (Map) fieldsCache.get(beanType.getTypeName());
		if(fields == null){
			fields = new HashMap(20);
			fieldsCache.put(beanType.getTypeName(),fields);
		}
		return fields;	
	}
	
	/**
	 * Get the map of IProxyBeanTypes for a beantype name. Meant to be used only in conjunction with REMSgtandardBeanTypeFactory.
	 * It is here so the REMStandardBeanTypeFactory can store pending proxies per expression.
	 * 
	 * @param beanType
	 * @return
	 * 
	 * @since 1.1.0
	 */
	public IProxyBeanType getBeanType(String beanTypeName) {
		if (beanTypeCache == null)
			beanTypeCache = new HashMap();
		return (IProxyBeanType) beanTypeCache.get(beanTypeName);
	}
	
	/**
	 * Add the beantype expression proxy to the map of bean type expression proxies. Used in conjunction with REMStandardBeanTypeFactory.
	 * It is here so the REMStandardBeanTypeFactory can store pending proxies per expression.
	 * @param beanTypeName
	 * @param beantype
	 * 
	 * @since 1.1.0
	 */
	public void addBeanType(String beanTypeName, IProxyBeanType beantype) {
		beanTypeCache.put(beanTypeName, beantype);
	}
	
	/**
	 * Remove the beantype expression proxy from the map. This is called because there was a rollback due to an endmark.
	 * @param beanTypeName
	 * 
	 * @since 1.1.0
	 */
	public void removeBeanType(String beanTypeName) {
		beanTypeCache.remove(beanTypeName);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jem.internal.proxy.core.Expression#pushIfTestToProxy()
	 */
	protected void pushIfTestToProxy() {
		processPendingTransactions();
		IREMExpressionConnection connection = getConnection();
		try {
			// Format of push if test to proxy command is:
			//	PushExpressionCommand(push if test to proxy)
			connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.IF_TEST_EXPRESSION_VALUE);
		} catch (IOException e) {
			connection.close();
			ProxyPlugin.getPlugin().getLogger().log(e);
			markInvalid(e.getLocalizedMessage());
			throwIllegalStateException(IO_EXCEPTION_MSG);
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jem.internal.proxy.core.Expression#pushIfElseToProxy(org.eclipse.jem.internal.proxy.initParser.tree.InternalIfElseOperandType)
	 */
	protected void pushIfElseToProxy(InternalIfElseOperandType clauseType) {
		processPendingTransactions();
		IREMExpressionConnection connection = getConnection();
		try {
			// Format of push conditional to proxy command is:
			//	PushExpressionCommand(push if/else clause to proxy) followed by:
			//		byte: clause type
			connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.IF_ELSE_EXPRESSION_VALUE);
			connection.pushByte((byte) clauseType.getValue());
		} catch (IOException e) {
			connection.close();
			ProxyPlugin.getPlugin().getLogger().log(e);
			markInvalid(e.getLocalizedMessage());
			throwIllegalStateException(IO_EXCEPTION_MSG);
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jem.internal.proxy.core.Expression#pushNewInstanceToProxy(java.lang.String, org.eclipse.jem.internal.proxy.core.IProxyBeanType)
	 */
	protected void pushNewInstanceToProxy(String initializationString, IProxyBeanType resultType) {
		processPendingTransactions();
		IREMExpressionConnection connection = getConnection();
		try {
			// Format of push new instance from initstring to proxy command is:
			//	PushExpressionCommand(push new instance to proxy) followed by:
			//		string: init string
			//		ValueObject: containing the rendered bean type proxy or the expression proxy for the declaring type
			connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.NEW_INSTANCE_VALUE);
			connection.pushString(initializationString);
			fillProxy(resultType, workerValue);
			connection.pushValueObject(workerValue);			
		} catch (IOException e) {
			connection.close();
			ProxyPlugin.getPlugin().getLogger().log(e);
			markInvalid(e.getLocalizedMessage());
			throwIllegalStateException(IO_EXCEPTION_MSG);
		} catch (CommandException e) {
			ProxyPlugin.getPlugin().getLogger().log(e);
			markInvalid(e.getLocalizedMessage());
			if (!e.isRecoverable()) {
				connection.close();
				throwIllegalStateException(COMMAND_EXCEPTION_MSG);
			}			
		}
	}

	private static class Mark extends PendingTransaction {
		public int markID;
		
		public Mark(int markID) {
			this.markID = markID;
		}
		
		public void pushTransaction(REMExpression remExpression) {
			IREMExpressionConnection connection = remExpression.getConnection();
			try {
				// Format of push mark to proxy command is:
				//	PushExpressionCommand(push mark to proxy) followed by:
				//		int: markID
				connection.pushExpressionCommand(remExpression.getREMExpressionID(), (byte)InternalExpressionTypes.MARK_VALUE);
				connection.pushInt(markID);
			} catch (IOException e) {
				connection.close();
				ProxyPlugin.getPlugin().getLogger().log(e);
				remExpression.markInvalid(e.getLocalizedMessage());
				remExpression.throwIllegalStateException(IO_EXCEPTION_MSG);
			}
		}
		
	}
	
	protected void pushMarkToProxy(int markID) {
		addPendingTransaction(new Mark(markID));
	}

	protected void pushEndmarkToProxy(int markID, boolean restore) {
		// See if the top pending transaction is now Mark(markID). If it is, then this transaction and the mark begin
		// can be thrown away because they are an empty block.
		PendingTransaction topEntry = getPendingEntryFromTop(1);
		if (topEntry instanceof Mark && ((Mark) topEntry).markID == markID) {
			popPendingEntry(1);
			return;
		}
		processPendingTransactions();
		IREMExpressionConnection connection = getConnection();
		try {
			// Format of push end mark to proxy command is:
			//	PushExpressionCommand(push end mark to proxy) followed by:
			//		int: markID
			//		boolean: restore
			connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.ENDMARK_VALUE);
			connection.pushInt(markID);
			connection.pushBoolean(restore);
		} catch (IOException e) {
			connection.close();
			ProxyPlugin.getPlugin().getLogger().log(e);
			markInvalid(e.getLocalizedMessage());
			throwIllegalStateException(IO_EXCEPTION_MSG);
		}
	}

	// This is the expression processor controller used to transfer.
	// This is the guy that maintains continuity of the transaction as
	// it is passed from one connection to another.
	protected IBeanProxy expressionProcesserController;	
	protected void pushBeginTransferThreadToProxy() throws ThrowableProxy {
		// If the controller is not null, that means we had already requested a transfer
		// but had not used it in this thread so there is no need to do anything. It
		// will be handled when switching back to the other thread.
		// If the connection is null, no need to do anything since there is no connection
		// to transfer.
		if (connection != null && expressionProcesserController == null) {
			IREMExpressionConnection lclConnection = getConnection();
			markInTransaction(lclConnection);
			try {
				workerValue.set();
				lclConnection.transferExpression(getREMExpressionID(), workerValue);
				expressionProcesserController = getREMBeanProxyFactory().getBeanProxy(workerValue);
				getREMRegistry().returnConnection(lclConnection);
				this.connection = null;
			} catch (CommandException e) {
				ProxyPlugin.getPlugin().getLogger().log(e);
				if (!e.isRecoverable()) {
					lclConnection.close();
					throwIllegalStateException(COMMAND_EXCEPTION_MSG);
				}			
			} finally {
				markEndTransaction(lclConnection);
			}
		}
	}

	protected void pushTransferThreadToProxy() {
		// Don't need to do anything. The next time we need to push data across, we will get a connection and the getConnection()
		// will hook up the expression processor controller for us. This way if nothing happens in this thread then we won't
		// waste communication time on it.
	}

	private static class SubexpressionBegin extends PendingTransaction {
		public int subexpressionNumber;
		
		public SubexpressionBegin(int subexpressionNumber) {
			this.subexpressionNumber = subexpressionNumber;
		}
		
		public void pushTransaction(REMExpression remExpression) {
			IREMExpressionConnection connection = remExpression.getConnection();
			try {
				// Format of push to subexpression begin proxy command is:
				//	PushExpressionCommand(push subexpression begin proxy to proxy) followed by:
				//		int: subexpression id
				connection.pushExpressionCommand(remExpression.getREMExpressionID(), (byte)InternalExpressionTypes.SUBEXPRESSION_BEGIN_EXPRESSION_VALUE);
				connection.pushInt(subexpressionNumber);
			} catch (IOException e) {
				connection.close();
				ProxyPlugin.getPlugin().getLogger().log(e);
				remExpression.markInvalid(e.getLocalizedMessage());
				remExpression.throwIllegalStateException(IO_EXCEPTION_MSG);
			}			
		}
		
	}

	protected void pushSubexpressionBeginToProxy(int subexpressionNumber) {
		addPendingTransaction(new SubexpressionBegin(subexpressionNumber));
	}

	protected void pushSubexpressionEndToProxy(int subexpressionNumber) {
		// See if the top pending transactions is SubexpressionBegin(subexpressionNumber). If it is then the SubexpressionBegin can be thrown away.
		PendingTransaction topEntry = getPendingEntryFromTop(1);
		if (topEntry instanceof SubexpressionBegin && ((SubexpressionBegin) topEntry).subexpressionNumber == subexpressionNumber) {
			popPendingEntry(1);
			return;
		}
		processPendingTransactions();
		IREMExpressionConnection connection = getConnection();
		try {
			// Format of push to block end proxy command is:
			//	PushExpressionCommand(push subexpression end proxy to proxy) followed by:
			//		int: subexpression id
			connection.pushExpressionCommand(getREMExpressionID(), (byte)InternalExpressionTypes.SUBEXPRESSION_END_EXPRESSION_VALUE);
			connection.pushInt(subexpressionNumber);
		} catch (IOException e) {
			connection.close();
			ProxyPlugin.getPlugin().getLogger().log(e);
			markInvalid(e.getLocalizedMessage());
			throwIllegalStateException(IO_EXCEPTION_MSG);
		}
		}
}
