// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

package jodd.madvoc.config;

import jodd.madvoc.ActionHandler;
import jodd.madvoc.filter.ActionFilter;
import jodd.madvoc.interceptor.ActionInterceptor;
import jodd.madvoc.result.ActionResult;

import java.lang.reflect.Method;

/**
 * Action runtime configuration and shared run-time data, used internally.
 */
public class ActionRuntime {

	// configuration
	private final ActionHandler actionHandler;
	private final Class actionClass;
	private final Method actionClassMethod;
	private final Class<? extends ActionResult> actionResult;
	private final Class<? extends ActionResult> defaultActionResult;
	private final String actionPath;
	private final String actionMethod;
	private final String resultBasePath;
	private final boolean async;
	private final boolean auth;

	// scope data
	private final ScopeData scopeData;
	private final MethodParam[] methodParams;

	private final boolean hasArguments;

	// run-time data
	private RouteChunk routeChunk;
	private final ActionFilter[] filters;
	private final ActionInterceptor[] interceptors;

	public ActionRuntime(
			final ActionHandler actionHandler,
			final Class actionClass,
			final Method actionClassMethod,
			final ActionFilter[] filters,
			final ActionInterceptor[] interceptors,
			final ActionDefinition actionDefinition,
			final Class<? extends ActionResult> actionResult,
			final Class<? extends ActionResult> defaultActionResult,
			final boolean async,
			final boolean auth,
			final ScopeData scopeData,
			final MethodParam[] methodParams
	) {
		this.actionHandler = actionHandler;
		this.actionClass = actionClass;
		this.actionClassMethod = actionClassMethod;
//		this.actionClassMethod = methodOfDeclaredClass(actionClass, actionClassMethod);
		this.actionPath = actionDefinition.actionPath();
		this.actionMethod = actionDefinition.actionMethod() == null ? null : actionDefinition.actionMethod().toUpperCase();
		this.resultBasePath = actionDefinition.resultBasePath();
		this.hasArguments = actionClassMethod != null && (actionClassMethod.getParameterTypes().length != 0);
		this.actionResult = actionResult;
		this.defaultActionResult = defaultActionResult;
		this.async = async;
		this.auth = auth;

		this.scopeData = scopeData;

		this.filters = filters;
		this.interceptors = interceptors;
		this.methodParams = methodParams;
	}

/*

	/**
	 * When wrapper proxy is used, the method will not belong to a type.
	 * This method finds the corresponding method in the type.
	private Method methodOfDeclaredClass(final Class type, final Method method) {
		if (method.getDeclaringClass() == type) {
			return method;
		}

		final Method[] allMethods = type.getDeclaredMethods();
		for (final Method newMethod : allMethods) {
			if (!newMethod.getName().equals(method.getName())) {
				continue;
			}
			if (!newMethod.getReturnType().equals(method.getReturnType())) {
				continue;
			}
			if (!Arrays.equals(newMethod.getParameterTypes(), method.getParameterTypes())) {
				continue;
			}
			return newMethod;
		}
		return method;
	}
*/

	// ---------------------------------------------------------------- getters

	/**
	 * Returns {@code true} if action handler is defined.
	 */
	public boolean isActionHandlerDefined() {
		return actionHandler != null;
	}

	/**
	 * Returns action handler.
	 */
	public ActionHandler getActionHandler() {
		return actionHandler;
	}

	/**
	 * Returns action class.
	 */
	public Class getActionClass() {
		return actionClass;
	}

	/**
	 * Returns action class method.
	 */
	public Method getActionClassMethod() {
		return actionClassMethod;
	}

	/**
	 * Returns action path.
	 */
	public String getActionPath() {
		return actionPath;
	}

	/**
	 * Returns action method.
	 */
	public String getActionMethod() {
		return actionMethod;
	}

	/**
	 * Returns action result base path.
	 */
	public String getResultBasePath() {
		return resultBasePath;
	}

	/**
	 * Returns interceptor instances.
	 */
	public ActionInterceptor[] getInterceptors() {
		return interceptors;
	}

	/**
	 * Returns filters instances.
	 */
	public ActionFilter[] getFilters() {
		return filters;
	}

	/**
	 * Returns true if action is asynchronous.
	 */
	public boolean isAsync() {
		return async;
	}

	public boolean isAuthenticated() {
		return auth;
	}

	/**
	 * Returns method parameters information, or null if method has no params.
	 */
	public MethodParam[] getMethodParams() {
		return methodParams;
	}

	/**
	 * Returns action result class that will render the result.
	 * may be null.
	 */
	public Class<? extends ActionResult> getActionResult() {
		return actionResult;
	}

	/**
	 * Returns default action result.
	 */
	public Class<? extends ActionResult> getDefaultActionResult() {
		return defaultActionResult;
	}

	/**
	 * Returns {@code true} if action has arguments.
	 */
	public boolean hasArguments() {
		return hasArguments;
	}

	/**
	 * Returns scope data.
	 */
	public ScopeData getScopeData() {
		return scopeData;
	}

	// ---------------------------------------------------------------- bind

	/**
	 * Binds a route chunk to this configuration.
	 */
	public void bind(final RouteChunk routeChunk) {
		this.routeChunk = routeChunk;
	}

	/**
	 * Returns route chunk associated with this configuration.
	 */
	public RouteChunk getRouteChunk() {
		return routeChunk;
	}

	// ---------------------------------------------------------------- to string

	/**
	 * Returns action string in form 'actionClass#actionMethod'.
	 */
	public String createActionString() {
		if (actionHandler != null) {
			return actionHandler.getClass().getName();
		}
		String className = actionClass.getName();

		final int ndx = className.indexOf("$$");

		if (ndx != -1) {
			className = className.substring(0, ndx);
		}

		return className + '#' + actionClassMethod.getName();
	}

	@Override
	public String toString() {
		return "action: " + actionPath + (actionMethod == null ? "" : '#' + actionMethod) + "  -->  " + createActionString();
	}

}


// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

package jodd.db.type;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class IntegerSqlType extends NullAwareSqlType<Integer> {

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Integer get(final ResultSet rs, final int index, final int dbSqlType) throws SQLException {
		return Integer.valueOf(rs.getInt(index));
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void set(final PreparedStatement st, final int index, final Integer value, final int dbSqlType) throws SQLException {
		st.setInt(index, value.intValue());
	}
}


// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

package jodd.petite.resolver;

import jodd.introspector.ClassDescriptor;
import jodd.introspector.ClassIntrospector;
import jodd.introspector.PropertyDescriptor;
import jodd.petite.def.BeanReferences;
import jodd.petite.def.PropertyInjectionPoint;
import jodd.util.ClassUtil;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * Resolves properties.
 */
public class PropertyResolver {

	protected final ReferencesResolver referencesResolver;

	public PropertyResolver(final ReferencesResolver referencesResolver) {
		this.referencesResolver = referencesResolver;
	}

	/**
	 * Resolves all properties for given type.
	 */
	public PropertyInjectionPoint[] resolve(Class type, final boolean autowire) {
		final List<PropertyInjectionPoint> list = new ArrayList<>();
		final Set<String> usedPropertyNames = new HashSet<>();

		// lookup fields
		while (type != Object.class) {

			final ClassDescriptor cd = ClassIntrospector.get().lookup(type);
			final PropertyDescriptor[] allPropertyDescriptors = cd.getAllPropertyDescriptors();

			for (PropertyDescriptor propertyDescriptor : allPropertyDescriptors) {

				if (propertyDescriptor.isGetterOnly()) {
					continue;
				}

				if (usedPropertyNames.contains(propertyDescriptor.getName())) {
					continue;
				}

				Class propertyType = propertyDescriptor.getType();
				if (ClassUtil.isTypeOf(propertyType, Collection.class)) {
					continue;
				}

				BeanReferences reference = referencesResolver.readReferenceFromAnnotation(propertyDescriptor);

				if (reference == null) {
					if (!autowire) {
						continue;
					} else {
						reference = referencesResolver.buildDefaultReference(propertyDescriptor);
					}
				}

				list.add(new PropertyInjectionPoint(propertyDescriptor, reference));

				usedPropertyNames.add(propertyDescriptor.getName());
			}

			// go to the supertype
			type = type.getSuperclass();
		}

		final PropertyInjectionPoint[] fields;

		if (list.isEmpty()) {
			fields = PropertyInjectionPoint.EMPTY;
		} else {
			fields = list.toArray(new PropertyInjectionPoint[0]);
		}

		return fields;
	}

}


// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

package jodd.http.net;

import jodd.http.HttpException;
import jodd.http.ProxyInfo;
import jodd.http.Sockets;

import javax.net.SocketFactory;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;

/**
 * Socket factory for SOCKS4 proxy. This proxy does not do password authentication.
 *
 * See: http://www.openssh.com/txt/socks5.protocol for more details.
 */
public class Socks4ProxySocketFactory extends SocketFactory {

	private final ProxyInfo proxy;
	private final int connectionTimeout;

	public Socks4ProxySocketFactory(final ProxyInfo proxy, final int connectionTimeout) {
		this.proxy = proxy;
		this.connectionTimeout = connectionTimeout;
	}

	@Override
	public Socket createSocket(final String host, final int port) {
		return createSocks4ProxySocket(host, port);
	}

	@Override
	public Socket createSocket(final String host, final int port, final InetAddress localHost, final int localPort) {
		return createSocks4ProxySocket(host, port);
	}

	@Override
	public Socket createSocket(final InetAddress host, final int port) {
		return createSocks4ProxySocket(host.getHostAddress(), port);
	}

	@Override
	public Socket createSocket(final InetAddress address, final int port, final InetAddress localAddress, final int localPort) {
		return createSocks4ProxySocket(address.getHostAddress(), port);
	}

	/**
	 * Connects to the SOCKS4 proxy and returns proxified socket.
	 */
	private Socket createSocks4ProxySocket(final String host, final int port) {
		Socket socket = null;
		final String proxyHost = proxy.getProxyAddress();
		final int proxyPort = proxy.getProxyPort();
		final String user = proxy.getProxyUsername();

		try {
			socket = Sockets.connect(proxyHost, proxyPort, connectionTimeout);

			final InputStream in = socket.getInputStream();
			final OutputStream out = socket.getOutputStream();

			socket.setTcpNoDelay(true);

			byte[] buf = new byte[1024];

			// 1) CONNECT

			int index = 0;
			buf[index++] = 4;
			buf[index++] = 1;

			buf[index++] = (byte) (port >>> 8);
			buf[index++] = (byte) (port & 0xff);

			InetAddress addr = InetAddress.getByName(host);
			byte[] byteAddress = addr.getAddress();
			for (byte byteAddres : byteAddress) {
				buf[index++] = byteAddres;
			}

			if (user != null) {
				System.arraycopy(user.getBytes(), 0, buf, index, user.length());
				index += user.length();
			}
			buf[index++] = 0;
			out.write(buf, 0, index);

			// 2) RESPONSE

			int len = 6;
			int s = 0;
			while (s < len) {
				int i = in.read(buf, s, len - s);
				if (i <= 0) {
					throw new HttpException(ProxyInfo.ProxyType.SOCKS4, "stream is closed");
				}
				s += i;
			}
			if (buf[0] != 0) {
				throw new HttpException(ProxyInfo.ProxyType.SOCKS4, "proxy returned VN " + buf[0]);
			}
			if (buf[1] != 90) {
				try {
					socket.close();
				} catch (Exception ignore) {
				}
				throw new HttpException(ProxyInfo.ProxyType.SOCKS4, "proxy returned CD " + buf[1]);
			}

			byte[] temp = new byte[2];
			in.read(temp, 0, 2);

			return socket;
		} catch (RuntimeException rtex) {
			closeSocket(socket);
			throw rtex;
		} catch (Exception ex) {
			closeSocket(socket);
			throw new HttpException(ProxyInfo.ProxyType.SOCKS4, ex.toString(), ex);
		}
	}

	/**
	 * Closes socket silently.
	 */
	private void closeSocket(final Socket socket) {
		try {
			if (socket != null) {
				socket.close();
			}
		} catch (Exception ignore) {
		}
	}
}


// ASM: a very small and fast Java bytecode manipulation framework
// Copyright (c) 2000-2011 INRIA, France Telecom
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
// 3. Neither the name of the copyright holders nor the names of its
//    contributors may be used to endorse or promote products derived from
//    this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
// THE POSSIBILITY OF SUCH DAMAGE.
package jodd.asm7;

/**
 * A non standard class, field, method or code attribute, as defined in the Java Virtual Machine
 * Specification (JVMS).
 *
 * @see JVMS
 *     4.7
 * @see JVMS
 *     4.7.3
 * @author Eric Bruneton
 * @author Eugene Kuleshov
 */
public class Attribute {

  /** The type of this attribute, also called its name in the JVMS. */
  public final String type;

  /**
   * The raw content of this attribute, only used for unknown attributes (see {@link #isUnknown()}).
   * The 6 header bytes of the attribute (attribute_name_index and attribute_length) are not
   * included.
   */
  private byte[] content;

  /**
   * The next attribute in this attribute list (Attribute instances can be linked via this field to
   * store a list of class, field, method or code attributes). May be {@literal null}.
   */
  Attribute nextAttribute;

  /**
   * Constructs a new empty attribute.
   *
   * @param type the type of the attribute.
   */
  protected Attribute(final String type) {
    this.type = type;
  }

  /**
   * Returns {@literal true} if this type of attribute is unknown. This means that the attribute
   * content can't be parsed to extract constant pool references, labels, etc. Instead, the
   * attribute content is read as an opaque byte array, and written back as is. This can lead to
   * invalid attributes, if the content actually contains constant pool references, labels, or other
   * symbolic references that need to be updated when there are changes to the constant pool, the
   * method bytecode, etc. The default implementation of this method always returns {@literal true}.
   *
   * @return {@literal true} if this type of attribute is unknown.
   */
  public boolean isUnknown() {
    return true;
  }

  /**
   * Returns {@literal true} if this type of attribute is a code attribute.
   *
   * @return {@literal true} if this type of attribute is a code attribute.
   */
  public boolean isCodeAttribute() {
    return false;
  }

  /**
   * Returns the labels corresponding to this attribute.
   *
   * @return the labels corresponding to this attribute, or {@literal null} if this attribute is not
   *     a code attribute that contains labels.
   */
  protected Label[] getLabels() {
    return new Label[0];
  }

  /**
   * Reads a {@link #type} attribute. This method must return a new {@link Attribute} object,
   * of type {@link #type}, corresponding to the 'length' bytes starting at 'offset', in the given
   * ClassReader.
   *
   * @param classReader the class that contains the attribute to be read.
   * @param offset index of the first byte of the attribute's content in {@link ClassReader#b}. The
   *     6 attribute header bytes (attribute_name_index and attribute_length) are not taken into
   *     account here.
   * @param length the length of the attribute's content (excluding the 6 attribute header bytes).
   * @param charBuffer the buffer to be used to call the ClassReader methods requiring a
   *     'charBuffer' parameter.
   * @param codeAttributeOffset index of the first byte of content of the enclosing Code attribute
   *     in {@link ClassReader#b}, or -1 if the attribute to be read is not a code attribute. The 6
   *     attribute header bytes (attribute_name_index and attribute_length) are not taken into
   *     account here.
   * @param labels the labels of the method's code, or {@literal null} if the attribute to be read
   *     is not a code attribute.
   * @return a new {@link Attribute} object corresponding to the specified bytes.
   */
  protected Attribute read(
      final ClassReader classReader,
      final int offset,
      final int length,
      final char[] charBuffer,
      final int codeAttributeOffset,
      final Label[] labels) {
    Attribute attribute = new Attribute(type);
    attribute.content = new byte[length];
    System.arraycopy(classReader.b, offset, attribute.content, 0, length);
    return attribute;
  }

  /**
   * Returns the byte array form of the content of this attribute. The 6 header bytes
   * (attribute_name_index and attribute_length) must not be added in the returned
   * ByteVector.
   *
   * @param classWriter the class to which this attribute must be added. This parameter can be used
   *     to add the items that corresponds to this attribute to the constant pool of this class.
   * @param code the bytecode of the method corresponding to this code attribute, or {@literal null}
   *     if this attribute is not a code attribute. Corresponds to the 'code' field of the Code
   *     attribute.
   * @param codeLength the length of the bytecode of the method corresponding to this code
   *     attribute, or 0 if this attribute is not a code attribute. Corresponds to the 'code_length'
   *     field of the Code attribute.
   * @param maxStack the maximum stack size of the method corresponding to this code attribute, or
   *     -1 if this attribute is not a code attribute.
   * @param maxLocals the maximum number of local variables of the method corresponding to this code
   *     attribute, or -1 if this attribute is not a code attribute.
   * @return the byte array form of this attribute.
   */
  protected ByteVector write(
      final ClassWriter classWriter,
      final byte[] code,
      final int codeLength,
      final int maxStack,
      final int maxLocals) {
    return new ByteVector(content);
  }

  /**
   * Returns the number of attributes of the attribute list that begins with this attribute.
   *
   * @return the number of attributes of the attribute list that begins with this attribute.
   */
  final int getAttributeCount() {
    int count = 0;
    Attribute attribute = this;
    while (attribute != null) {
      count += 1;
      attribute = attribute.nextAttribute;
    }
    return count;
  }

  /**
   * Returns the total size in bytes of all the attributes in the attribute list that begins with
   * this attribute. This size includes the 6 header bytes (attribute_name_index and
   * attribute_length) per attribute. Also adds the attribute type names to the constant pool.
   *
   * @param symbolTable where the constants used in the attributes must be stored.
   * @return the size of all the attributes in this attribute list. This size includes the size of
   *     the attribute headers.
   */
  final int computeAttributesSize(final SymbolTable symbolTable) {
    final byte[] code = null;
    final int codeLength = 0;
    final int maxStack = -1;
    final int maxLocals = -1;
    return computeAttributesSize(symbolTable, code, codeLength, maxStack, maxLocals);
  }

  /**
   * Returns the total size in bytes of all the attributes in the attribute list that begins with
   * this attribute. This size includes the 6 header bytes (attribute_name_index and
   * attribute_length) per attribute. Also adds the attribute type names to the constant pool.
   *
   * @param symbolTable where the constants used in the attributes must be stored.
   * @param code the bytecode of the method corresponding to these code attributes, or {@literal
   *     null} if they are not code attributes. Corresponds to the 'code' field of the Code
   *     attribute.
   * @param codeLength the length of the bytecode of the method corresponding to these code
   *     attributes, or 0 if they are not code attributes. Corresponds to the 'code_length' field of
   *     the Code attribute.
   * @param maxStack the maximum stack size of the method corresponding to these code attributes, or
   *     -1 if they are not code attributes.
   * @param maxLocals the maximum number of local variables of the method corresponding to these
   *     code attributes, or -1 if they are not code attribute.
   * @return the size of all the attributes in this attribute list. This size includes the size of
   *     the attribute headers.
   */
  final int computeAttributesSize(
      final SymbolTable symbolTable,
      final byte[] code,
      final int codeLength,
      final int maxStack,
      final int maxLocals) {
    final ClassWriter classWriter = symbolTable.classWriter;
    int size = 0;
    Attribute attribute = this;
    while (attribute != null) {
      symbolTable.addConstantUtf8(attribute.type);
      size += 6 + attribute.write(classWriter, code, codeLength, maxStack, maxLocals).length;
      attribute = attribute.nextAttribute;
    }
    return size;
  }

  /**
   * Puts all the attributes of the attribute list that begins with this attribute, in the given
   * byte vector. This includes the 6 header bytes (attribute_name_index and attribute_length) per
   * attribute.
   *
   * @param symbolTable where the constants used in the attributes must be stored.
   * @param output where the attributes must be written.
   */
  final void putAttributes(final SymbolTable symbolTable, final ByteVector output) {
    final byte[] code = null;
    final int codeLength = 0;
    final int maxStack = -1;
    final int maxLocals = -1;
    putAttributes(symbolTable, code, codeLength, maxStack, maxLocals, output);
  }

  /**
   * Puts all the attributes of the attribute list that begins with this attribute, in the given
   * byte vector. This includes the 6 header bytes (attribute_name_index and attribute_length) per
   * attribute.
   *
   * @param symbolTable where the constants used in the attributes must be stored.
   * @param code the bytecode of the method corresponding to these code attributes, or {@literal
   *     null} if they are not code attributes. Corresponds to the 'code' field of the Code
   *     attribute.
   * @param codeLength the length of the bytecode of the method corresponding to these code
   *     attributes, or 0 if they are not code attributes. Corresponds to the 'code_length' field of
   *     the Code attribute.
   * @param maxStack the maximum stack size of the method corresponding to these code attributes, or
   *     -1 if they are not code attributes.
   * @param maxLocals the maximum number of local variables of the method corresponding to these
   *     code attributes, or -1 if they are not code attribute.
   * @param output where the attributes must be written.
   */
  final void putAttributes(
      final SymbolTable symbolTable,
      final byte[] code,
      final int codeLength,
      final int maxStack,
      final int maxLocals,
      final ByteVector output) {
    final ClassWriter classWriter = symbolTable.classWriter;
    Attribute attribute = this;
    while (attribute != null) {
      ByteVector attributeContent =
          attribute.write(classWriter, code, codeLength, maxStack, maxLocals);
      // Put attribute_name_index and attribute_length.
      output.putShort(symbolTable.addConstantUtf8(attribute.type)).putInt(attributeContent.length);
      output.putByteArray(attributeContent.data, 0, attributeContent.length);
      attribute = attribute.nextAttribute;
    }
  }

  /** A set of attribute prototypes (attributes with the same type are considered equal). */
  static final class Set {

    private static final int SIZE_INCREMENT = 6;

    private int size;
    private Attribute[] data = new Attribute[SIZE_INCREMENT];

    void addAttributes(final Attribute attributeList) {
      Attribute attribute = attributeList;
      while (attribute != null) {
        if (!contains(attribute)) {
          add(attribute);
        }
        attribute = attribute.nextAttribute;
      }
    }

    Attribute[] toArray() {
      Attribute[] result = new Attribute[size];
      System.arraycopy(data, 0, result, 0, size);
      return result;
    }

    private boolean contains(final Attribute attribute) {
      for (int i = 0; i < size; ++i) {
        if (data[i].type.equals(attribute.type)) {
          return true;
        }
      }
      return false;
    }

    private void add(final Attribute attribute) {
      if (size >= data.length) {
        Attribute[] newData = new Attribute[data.length + SIZE_INCREMENT];
        System.arraycopy(data, 0, newData, 0, size);
        data = newData;
      }
      data[size++] = attribute;
    }
  }
}


// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

package jodd.madvoc.component;

import jodd.cache.TypeCache;
import jodd.log.Logger;
import jodd.log.LoggerFactory;
import jodd.madvoc.ActionRequest;
import jodd.madvoc.MadvocException;
import jodd.madvoc.config.ActionRuntime;
import jodd.madvoc.meta.RenderWith;
import jodd.madvoc.result.ActionResult;
import jodd.petite.meta.PetiteInject;
import jodd.util.ClassUtil;

import java.util.HashSet;
import java.util.Set;

/**
 * Manager for Madvoc result handlers.
 */
public class ResultsManager {

	private static final Logger log = LoggerFactory.getLogger(ResultsManager.class);

	@PetiteInject
	protected ContextInjectorComponent contextInjectorComponent;

	public ResultsManager() {
		this.allResults = TypeCache.createDefault();
	}

	// ---------------------------------------------------------------- container

	protected final TypeCache<ActionResult> allResults;

	/**
	 * Returns all action results as new set.
	 */
	public Set<ActionResult> getAllActionResults() {
		final Set<ActionResult> set = new HashSet<>(allResults.size());
		allResults.forEachValue(set::add);
		return set;
	}

	/**
	 * Registers an action result handler and returns created {@link jodd.madvoc.result.ActionResult} if
	 * result with same type doesn't exist. Otherwise, returns existing result and created one will be ignored.
	 */
	public ActionResult register(final Class<? extends ActionResult> resultClass) {
		return register(createResult(resultClass));
	}

	/**
	 * Registers new action result instance. If action result of the same class is already
	 * registered, registration will be skipped. If result for the same result type or
	 * same target class exist, it will be replaced! However, default Jodd results will
	 * <i>never</i> replace other results. After the registration, results are initialized.
	 */
	protected ActionResult register(final ActionResult result) {
		Class<? extends ActionResult> actionResultClass = result.getClass();

		// check existing

		ActionResult existingResult = allResults.get(actionResultClass);

		if (existingResult != null) {
			if (log.isDebugEnabled()) {
				log.debug("ActionResult already registered: " + actionResultClass);
			}
			return existingResult;
		}

		allResults.put(actionResultClass, result);

		// + init

		initializeResult(result);

		return result;
	}

	// ---------------------------------------------------------------- lookup

	/**
	 * Lookups for action result and {@link #register(Class) registers} it if missing.
	 */
	private ActionResult lookupAndRegisterIfMissing(final Class<? extends ActionResult> actionResultClass) {
		ActionResult actionResult = allResults.get(actionResultClass);

		if (actionResult == null) {
			actionResult = register(actionResultClass);
		}

		return actionResult;
	}

	/**
	 * Lookups for {@link jodd.madvoc.result.ActionResult action result handler}
	 * based on current {@link jodd.madvoc.ActionRequest action request} and action method
	 * result object.
	 */
	public ActionResult lookup(final ActionRequest actionRequest, final Object resultObject) {

		ActionResult actionResultHandler = null;

		// + read @RenderWith value on method
		{
			final ActionRuntime actionRuntime = actionRequest.getActionRuntime();

			final Class<? extends ActionResult> actionResultClass = actionRuntime.getActionResult();

			if (actionResultClass != null) {
				actionResultHandler = lookupAndRegisterIfMissing(actionResultClass);
			}
		}

		// + use @RenderWith value on resulting object if exist
		if (actionResultHandler == null && resultObject != null) {
			final RenderWith renderWith = resultObject.getClass().getAnnotation(RenderWith.class);

			if (renderWith != null) {
				actionResultHandler = lookupAndRegisterIfMissing(renderWith.value());
			}
			else if (resultObject instanceof ActionResult) {
				// special case - returned value is already the ActionResult
				actionResultHandler = (ActionResult) resultObject;
			}
		}

		// + use action configuration
		if (actionResultHandler == null) {
			final ActionRuntime actionRuntime = actionRequest.getActionRuntime();

			final Class<? extends ActionResult> actionResultClass = actionRuntime.getDefaultActionResult();

			if (actionResultClass != null) {
				actionResultHandler = lookupAndRegisterIfMissing(actionResultClass);
			}
		}

		if (actionResultHandler == null) {
			throw new MadvocException("ActionResult not found for: " + resultObject);
		}

		// set action result object into action request!
		actionRequest.bindActionResult(resultObject);

		return actionResultHandler;
	}

	// ---------------------------------------------------------------- init

	/**
	 * Initializes action result.
	 */
	protected void initializeResult(final ActionResult result) {
		contextInjectorComponent.injectContext(result);
	}

	// ---------------------------------------------------------------- create

	/**
	 * Creates new {@link jodd.madvoc.result.ActionResult}.
	 */
	protected ActionResult createResult(final Class<? extends ActionResult> actionResultClass) {
		try {
			return ClassUtil.newInstance(actionResultClass);
		} catch (Exception ex) {
			throw new MadvocException("Invalid Madvoc result: " + actionResultClass, ex);
		}
	}

}


// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

package jodd.util;

public class CharSequenceUtil {

	public static boolean equals(final CharSequence charSequence1, final CharSequence charSequence2) {
		int len = charSequence1.length();

		if (len != charSequence2.length()) {
			return false;
		}

		for (int i = 0; i < len; i++) {
			if (charSequence1.charAt(i) != charSequence2.charAt(i)) {
				return false;
			}
		}

		return true;
	}

	public static boolean equalsToLowercase(final CharSequence charSequence, final CharSequence name) {
		int len = charSequence.length();

		if (len != name.length()) {
			return false;
		}

		if (compare(charSequence, name, len)) return false;

		return true;
	}

	public static boolean startsWithLowercase(final CharSequence charSequence, final CharSequence chars) {
		int length = chars.length();
		if (charSequence.length() < length) {
			return false;
		}

		if (compare(charSequence, chars, length)) return false;

		return true;
	}

	private static boolean compare(final CharSequence charSequence, final CharSequence chars, final int length) {
		for (int i = 0; i < length; i++) {
			char c = charSequence.charAt(i);

			c = CharUtil.toLowerAscii(c);

			if (c != chars.charAt(i)) {
				return true;
			}
		}
		return false;
	}

	public static boolean equalsIgnoreCase(final CharSequence charSequence1, final CharSequence charSequence2) {
		int len = charSequence1.length();

		if (len != charSequence2.length()) {
			return false;
		}

		for (int i = 0; i < len; i++) {
			char c1 = charSequence1.charAt(i);
			c1 = CharUtil.toLowerAscii(c1);

			char c2 = charSequence2.charAt(i);
			c2 = CharUtil.toLowerAscii(c2);

			if (c1 != c2) {
				return false;
			}
		}
		return true;
	}

	// ---------------------------------------------------------------- find

	/**
	 * Match if one character equals to any of the given character.
	 *
	 * @return true if characters match any character from given array,
	 *         otherwise false
	 */
	public static boolean equalsOne(final char c, final CharSequence match) {
		for (int i = 0; i < match.length(); i++) {
			char aMatch = match.charAt(i);
			if (c == aMatch) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Finds index of the first character in given charsequence the matches any from the
	 * given set of characters.
	 *
	 * @return index of matched character or -1
	 */
	public static int findFirstEqual(final CharSequence source, final int index, final CharSequence match) {
		for (int i = index; i < source.length(); i++) {
			if (equalsOne(source.charAt(i), match)) {
				return i;
			}
		}
		return -1;
	}

	/**
	 * Finds index of the first character in given array the matches any from the
	 * given set of characters.
	 *
	 * @return index of matched character or -1
	 */
	public static int findFirstEqual(final char[] source, final int index, final char match) {
		for (int i = index; i < source.length; i++) {
			if (source[i] == match) {
				return i;
			}
		}
		return -1;
	}

	/**
	 * Finds index of the first character in given charsequence the differs from the
	 * given set of characters.
	 *
	 * @return index of matched character or -1
	 */
	public static int findFirstDiff(final CharSequence source, final int index, final CharSequence match) {
		for (int i = index; i < source.length(); i++) {
			if (!equalsOne(source.charAt(i), match)) {
				return i;
			}
		}
		return -1;
	}

	/**
	 * Finds index of the first character in given array the differs from the
	 * given set of characters.
	 *
	 * @return index of matched character or -1
	 */
	public static int findFirstDiff(final char[] source, final int index, final char match) {
		for (int i = index; i < source.length; i++) {
			if (source[i] != match) {
				return i;
			}
		}
		return -1;
	}

}


// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

package jodd.util;

import jodd.io.StreamGobbler;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static jodd.util.ProcessRunner.ERROR_PREFIX;
import static jodd.util.ProcessRunner.OUTPUT_PREFIX;

/**
 * Simple user-friendly wrapper over {@code ProcessBuilder}. Has the following:
 *

 *
fluent interface

 *
no exception is throw

 *
output is collected to string

 *
easy environment manipulation

 *

 */
public class CommandLine {

	public static final int OK = 0;

	protected final List<String> cmdLine = new ArrayList<>();
	protected Map<String, String> env = null;
	protected boolean cleanEnvironment = false;
	protected File workingDirectory;
	protected String outPrefix = OUTPUT_PREFIX;
	protected String errPrefix = ERROR_PREFIX;
	protected OutputStream out = System.out;
	protected OutputStream err = System.err;

	// ---------------------------------------------------------------- ctor

	protected CommandLine(final String command) {
		cmdLine.add(command);
	}

	/**
	 * Creates command line with given command.
	 */
	public static CommandLine cmd(final String command) {
		return new CommandLine(command);
	}

	// ---------------------------------------------------------------- arguments

	/**
	 * Defines working directory.
	 */
	public CommandLine workingDirectory(final File workDirectory) {
		this.workingDirectory = workDirectory;

		return this;
	}

	/**
	 * Defines working directory.
	 */
	public CommandLine workingDirectory(final String workDirectory) {
		this.workingDirectory = new File(workDirectory);

		return this;
	}

	/**
	 * Adds single argument.
	 */
	public CommandLine arg(final String argument) {
		cmdLine.add(argument);

		return this;
	}

	/**
	 * Adds several arguments.
	 */
	public CommandLine args(final String... arguments) {
		if (arguments != null && arguments.length > 0) {
			Collections.addAll(cmdLine, arguments);
		}

		return this;
	}

	/**
	 * Defines output prefix.
	 */
	public CommandLine outPrefix(final String prefix) {
		this.outPrefix = prefix;
		return this;
	}

	/**
	 * Defines error prefix.
	 */
	public CommandLine errPrefix(final String prefix) {
		this.errPrefix = prefix;
		return this;
	}

	public CommandLine out(final OutputStream out) {
		this.out = out;
		return this;
	}

	public CommandLine err(final OutputStream err) {
		this.err = err;
		return this;
	}

	/**
	 * Sets environment variable.
	 */
	public CommandLine env(final String key, final String value) {
		if (env == null) {
			env = new HashMap<>();
		}
		env.put(key, value);
		return this;
	}

	/**
	 * When set to {@code true}, environment will not be copied from the
	 * parent process and will be completly empty.
	 */
	public CommandLine newEnv(final boolean clean) {
		cleanEnvironment = clean;
		return this;
	}

	// ---------------------------------------------------------------- execute

	/**
	 * Runs command and returns process result.
	 */
	public ProcessRunner.ProcessResult run() {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();

		out = err = baos;

		try {
			baos.write(StringUtil.join(cmdLine, ' ').getBytes());
			baos.write(StringPool.BYTES_NEW_LINE);
		}
		catch (IOException ignore) {
		}

		ProcessBuilder processBuilder = new ProcessBuilder();

		processBuilder.command(cmdLine);

		if (cleanEnvironment) {
			processBuilder.environment().clear();
		}
		if (env != null) {
			processBuilder.environment().putAll(env);
		}

		processBuilder.directory(workingDirectory);

		Process process = null;
		try {
			process = processBuilder.start();
		}
		catch (IOException ioex) {
			return writeException(baos, ioex);
		}

		StreamGobbler outputGobbler = new StreamGobbler(process.getInputStream(), out, outPrefix);
		StreamGobbler errorGobbler = new StreamGobbler(process.getErrorStream(), err, errPrefix);

		outputGobbler.start();
		errorGobbler.start();

		int result;

		try {
			result = process.waitFor();
		}
		catch (InterruptedException iex) {
			return writeException(baos, iex);
		}

		outputGobbler.waitFor();
		errorGobbler.waitFor();

		return new ProcessRunner.ProcessResult(result, baos.toString());
	}

	private ProcessRunner.ProcessResult writeException(final ByteArrayOutputStream baos, final Exception ex) {
		try {
			baos.write(errPrefix.getBytes());
		}
		catch (IOException ignore) {
		}

		ex.printStackTrace(new PrintStream(baos));

		return new ProcessRunner.ProcessResult(-1, baos.toString());
	}
}


// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

package jodd.madvoc.component;

import jodd.madvoc.MadvocException;

/**
 * Madvoc component lifecycle events.
 */
public class MadvocComponentLifecycle {

	/**
	 * Madvoc INIT handler.
	 * Components are being registered. Should not request for an instance during this phase, since dependencies
	 * may still not be registered or updated. May be used for registration of additional components.
	 */
	@FunctionalInterface
	public interface Init {
		void init();
	}

	/**
	 * Madvoc START handler.
	 * All components are registered. Web application is being loaded. Should not use any web application
	 * component (action, filter, interceptor, result...) during this phase. New web application components
	 * may be registered.
	 */
	@FunctionalInterface
	public interface Start {
		void start();
	}

	/**
	 * Madvoc READY handler.
	 * Initialization is done: everything is registered.
	 * This phase is used for running any initialization that depends on the complete configuration.
	 */
	@FunctionalInterface
	public interface Ready {
		void ready();
	}

	/**
	 *  Madvoc STOP handler. Invoked on Madvoc shutdown.
	 */
	@FunctionalInterface
	public interface Stop {
		void stop();
	}

	/**
	 * Invoke the listener based on type.
	 * Not very OOP, but works.
	 */
	public static void invoke(final Object listener, final Class listenerType) {
		if (listenerType == Init.class) {
			((Init) listener).init();
			return;
		}
		if (listenerType == Start.class) {
			((Start) listener).start();
			return;
		}
		if (listenerType == Ready.class) {
			((Ready) listener).ready();
			return;
		}
		if (listenerType == Stop.class) {
			((Stop) listener).stop();
			return;
		}
		throw new MadvocException("Invalid listener");
	}

}