// 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;

import jodd.io.StreamUtil;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Simple HTTP tunnel base ready to be extended.
 */
public class HttpTunnel {

	/**
	 * The number of threads that can be executed in parallel.
	 */
	protected int threadPoolSize = 10;

	/**
	 * Number of incoming sockets connection that can be hold
	 * before processing each.
	 */
	protected int socketBacklog = 100;

	/**
	 * Tunnel listening port.
	 */
	protected int listenPort = 8888;

	/**
	 * Target host.
	 */
	protected String targetHost = "localhost";

	/**
	 * Target port.
	 */
	protected int targetPort = 8080;

	protected ExecutorService executorService;
	protected volatile boolean running;
	protected ServerSocket serverSocket;

	/**
	 * Starts HTTP tunnel. Method ends when the tunnel is stopped.
	 */
	public void start() throws IOException {
		serverSocket = new ServerSocket(listenPort, socketBacklog);
		serverSocket.setReuseAddress(true);
		executorService = Executors.newFixedThreadPool(threadPoolSize);

		running = true;
		while (running) {
			Socket socket = serverSocket.accept();
			socket.setKeepAlive(false);
			executorService.execute(onSocketConnection(socket));
		}
		executorService.shutdown();
	}

	/**
	 * Invoked on incoming connection. By default returns {@link HttpTunnelConnection}
	 * to handle the connection. May be used to return custom
	 * handlers.
	 */
	protected Runnable onSocketConnection(final Socket socket) {
		return new HttpTunnelConnection(socket);
	}

	/**
	 * Stops the tunnel, shutdowns the thread pool and closes server socket.
	 */
	public void stop() {
		running = false;
		executorService.shutdown();
		try {
			serverSocket.close();
		} catch (IOException ignore) {
		}
	}

	/**
	 * Single connection handler that performs the tunneling.
	 */
	public class HttpTunnelConnection implements Runnable {

		protected final Socket socket;

		public HttpTunnelConnection(final Socket socket) {
			this.socket = socket;
		}

		@Override
		public void run() {
			try {
				tunnel();
			} catch (IOException ioex) {
				ioex.printStackTrace();
			}
		}

		/**
		 * Invoked after income connection is parsed. Nothing is
		 * changed in the request, except the target host and port.
		 */
		protected void onRequest(final HttpRequest request) {
		}

		/**
		 * Invoked after target response is processed. Response is now
		 * ready to be sent back to the client. The following header
		 * parameters are changed:
		 *

		 *
Transfer-Encoding is removed, as body is returned at once,

		 *
Content-Length is added/update to body size.

		 *

		 */
		protected void onResponse(final HttpResponse response) {
		}

		/**
		 * Performs the tunneling. The following steps occurs:
		 *

		 *
read and parse clients request

		 *
open socket to target

		 *
resend request to target

		 *
read targets response

		 *
fix response and resend it to client

		 *

		 */
		protected void tunnel() throws IOException {

			// read request
			InputStream socketInput = socket.getInputStream();
			HttpRequest request = HttpRequest.readFrom(socketInput);

			// open client socket to target
			final Socket clientSocket = Sockets.connect(targetHost, targetPort);

			// do request
			request.host(targetHost);
			request.port(targetPort);
			request.setHostHeader();
			onRequest(request);

			// resend request to target
			OutputStream out = clientSocket.getOutputStream();
			request.sendTo(out);

			// read target response
			InputStream in = clientSocket.getInputStream();
			HttpResponse response = HttpResponse.readFrom(in);

			// close client socket
			StreamUtil.close(in);
			StreamUtil.close(out);
			try {
				clientSocket.close();
			} catch (IOException ignore) {
			}

			// fix response
			if (response.body() != null) {
				response.headerRemove("Transfer-Encoding");
				response.contentLength(response.body().length());
			}

			// do response
			onResponse(response);

			// send response back
			OutputStream socketOutput = socket.getOutputStream();
			response.sendTo(socketOutput);

			// close socket
			StreamUtil.close(socketInput);
			StreamUtil.close(socketOutput);
			try {
				socket.close();
			} catch (IOException ignore) {
			}
		}
	}

}


// 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.typeconverter.impl;

import jodd.buffer.FastIntBuffer;
import jodd.typeconverter.TypeConverter;
import jodd.typeconverter.TypeConverterManager;
import jodd.util.StringUtil;

import java.util.Collection;

/**
 * Converts given object to int[].
 */
public class IntegerArrayConverter implements TypeConverter<int[]> {

	protected final TypeConverterManager typeConverterManager;

	public IntegerArrayConverter(final TypeConverterManager typeConverterManager) {
		this.typeConverterManager = typeConverterManager;
	}

	@Override
	public int[] convert(final Object value) {
		if (value == null) {
			return null;
		}

		final Class valueClass = value.getClass();

		if (!valueClass.isArray()) {
			// source is not an array
			return convertValueToArray(value);
		}

		// source is an array
		return convertArrayToArray(value);
	}

	/**
	 * Converts type using type converter manager.
	 */
	protected int convertType(final Object value) {
		return typeConverterManager.convertType(value, int.class).intValue();
	}

	/**
	 * Creates an array with single element.
	 */
	protected int[] convertToSingleElementArray(final Object value) {
		return new int[] {convertType(value)};
	}

	/**
	 * Converts non-array value to array. Detects various
	 * collection types and iterates them to make conversion
	 * and to create target array.
	 */
	protected int[] convertValueToArray(final Object value) {
		if (value instanceof Collection) {
			final Collection collection = (Collection) value;
			final int[] target = new int[collection.size()];

			int i = 0;
			for (final Object element : collection) {
				target[i] = convertType(element);
				i++;
			}

			return target;
		}

		if (value instanceof Iterable) {
			final Iterable iterable = (Iterable) value;

			final FastIntBuffer fastIntBuffer = new FastIntBuffer();

			for (final Object element : iterable) {
				final int convertedValue = convertType(element);
				fastIntBuffer.append(convertedValue);
			}

			return fastIntBuffer.toArray();
		}

		if (value instanceof CharSequence) {
			final String[] strings = StringUtil.splitc(value.toString(), ArrayConverter.NUMBER_DELIMITERS);
			return convertArrayToArray(strings);
		}

		// everything else:
		return convertToSingleElementArray(value);
	}

	/**
	 * Converts array value to array.
	 */
	protected int[] convertArrayToArray(final Object value) {
		final Class valueComponentType = value.getClass().getComponentType();

		final int[] result;

		if (valueComponentType.isPrimitive()) {
			result = convertPrimitiveArrayToArray(value, valueComponentType);
		} else {
			// convert object array to target array
			final Object[] array = (Object[]) value;
			result = new int[array.length];

			for (int i = 0; i < array.length; i++) {
				result[i] = convertType(array[i]);
			}
		}

		return result;
	}

	/**
	 * Converts primitive array to target array.
	 */
	protected int[] convertPrimitiveArrayToArray(final Object value, final Class primitiveComponentType) {
		int[] result = null;

		if (primitiveComponentType == int.class) {
			return (int[]) value;
		}

		if (primitiveComponentType == long.class) {
			final long[] array = (long[]) value;
			result = new int[array.length];
			for (int i = 0; i < array.length; i++) {
				result[i] = (int) array[i];
			}
		}
		else if (primitiveComponentType == float.class) {
			final float[] array = (float[]) value;
			result = new int[array.length];
			for (int i = 0; i < array.length; i++) {
				result[i] = (int) array[i];
			}
		}
		else if (primitiveComponentType == double.class) {
			final double[] array = (double[]) value;
			result = new int[array.length];
			for (int i = 0; i < array.length; i++) {
				result[i] = (int) array[i];
			}
		}
		else if (primitiveComponentType == short.class) {
			final short[] array = (short[]) value;
			result = new int[array.length];
			for (int i = 0; i < array.length; i++) {
				result[i] = (int) array[i];
			}
		}
		else if (primitiveComponentType == byte.class) {
			final byte[] array = (byte[]) value;
			result = new int[array.length];
			for (int i = 0; i < array.length; i++) {
				result[i] = (int) array[i];
			}
		}
		else if (primitiveComponentType == char.class) {
			final char[] array = (char[]) value;
			result = new int[array.length];
			for (int i = 0; i < array.length; i++) {
				result[i] = (int) array[i];
			}
		}
		else if (primitiveComponentType == boolean.class) {
			final boolean[] array = (boolean[]) value;
			result = new int[array.length];
			for (int i = 0; i < array.length; i++) {
				result[i] = array[i] ? 1 : 0;
			}
		}
		return result;
	}

}


// 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;

/**
 * Runtime utilities.
 */
public class ProcessRunner {

	public static final String ERROR_PREFIX = "err> ";
	public static final String OUTPUT_PREFIX = "out> ";

	public static class ProcessResult {

		private final int exitCode;
		private final String output;

		protected ProcessResult(final int existCode, final String output) {
			this.exitCode = existCode;
			this.output = output;
		}

		/**
		 * Returns process exit code.
		 */
		public int getExitCode() {
			return exitCode;
		}

		/**
		 * Returns process output.
		 */
		public String getOutput() {
			return output;
		}
	}

	/**
	 * Executes a process and returns the process output and exit code.
	 */
	public static ProcessResult run(final Process process) throws InterruptedException {
		final ByteArrayOutputStream baos = new ByteArrayOutputStream();

		final StreamGobbler outputGobbler = new StreamGobbler(process.getInputStream(), baos, OUTPUT_PREFIX);
		final StreamGobbler errorGobbler = new StreamGobbler(process.getErrorStream(), baos, ERROR_PREFIX);

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

		final int result = process.waitFor();

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

		return new ProcessResult(result, 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.mail;

import com.sun.mail.imap.IMAPSSLStore;
import jodd.util.StringPool;

import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.URLName;
import java.util.Properties;

/**
 * IMAP SSL Server.
 */
public class ImapSslServer extends ImapServer {

	/**
	 * Default IMAP SSL port.
	 */
	protected static final int DEFAULT_SSL_PORT = 993;

	public ImapSslServer(final Builder builder) {
		super(builder, DEFAULT_SSL_PORT);
	}

	@Override
	protected Properties createSessionProperties() {
		final Properties props = super.createSessionProperties();

		props.setProperty(MAIL_IMAP_SOCKET_FACTORY_PORT, String.valueOf(port));
		props.setProperty(MAIL_IMAP_SOCKET_FACTORY_CLASS, "javax.net.ssl.SSLSocketFactory");
		props.setProperty(MAIL_IMAP_SOCKET_FACTORY_FALLBACK, StringPool.FALSE);

		return props;
	}

	/**
	 * Returns email store.
	 *
	 * @param session {@link Session}
	 * @return {@link com.sun.mail.imap.IMAPSSLStore}
	 */
	@Override
	protected IMAPSSLStore getStore(final Session session) {
		SimpleAuthenticator simpleAuthenticator = (SimpleAuthenticator) authenticator;

		final URLName url;

		if (simpleAuthenticator == null) {
			url = new URLName(
				PROTOCOL_IMAP,
				host, port,
				StringPool.EMPTY, null, null);
		}
		else {
			final PasswordAuthentication pa = simpleAuthenticator.getPasswordAuthentication();
			url = new URLName(
				PROTOCOL_IMAP,
				host, port,
				StringPool.EMPTY,
				pa.getUserName(), pa.getPassword());
		}

		return new IMAPSSLStore(session, url);
	}

}


// 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.json;

import jodd.util.StringUtil;

import java.util.Arrays;

import static jodd.util.StringPool.STAR;

/**
 * Path query is used to match properties with some Path.
 * Query is expressed in dot notation. Each term between the dots
 * is a property name of a parent. Query may contains wildcard: '*' that
 * can be used instead of a named term.
 *

 * Included and excluded path matching works a bit differently. Included
 * query matches all sub-paths. Excluded query is strict and match only
 * paths with the same length.
 */
public class PathQuery {

	protected final String[] expression;
	protected final boolean wildcard;
	protected final boolean included;

	public PathQuery(final String expression, final boolean included) {
		this.expression = StringUtil.splitc(expression, '.');
		wildcard = expression.indexOf('*') >= 0;
		this.included = included;
	}

	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();

		builder.append('[');
		for (int i = 0; i < expression.length; i++) {
			if (i > 0) {
				builder.append('.');
			}
			builder.append(expression[i]);
		}
		builder.append(']');
		return builder.toString();
	}

	/**
	 * Returns true if path matches the query.
	 */
	public boolean matches(final Path path) {
		int exprNdx = 0;
		int pathNdx = 0;

		int pathLen = path.length();
		int exprLen = expression.length;

		while (pathNdx < pathLen) {
			CharSequence current = path.get(pathNdx);

			if (exprNdx < exprLen && expression[exprNdx].equals(STAR)) {
				exprNdx++;
			}
			else if (exprNdx < exprLen && expression[exprNdx].contentEquals(current)) {
				pathNdx++;
				exprNdx++;
			}
			else if (exprNdx - 1 >= 0 && expression[exprNdx - 1].equals(STAR)) {
				pathNdx++;
			}
			else {
				return false;
			}
		}

		if (exprNdx > 0 && expression[exprNdx - 1].equals(STAR)) {
			return pathNdx >= pathLen && exprNdx >= exprLen;
		}
		else {
			return pathLen != 0 &&
					pathNdx >= pathLen &&
					(included || exprNdx >= exprLen);
		}
	}

	/**
	 * Returns true if this query contains a wildcard.
	 */
	public boolean isWildcard() {
		return wildcard;
	}

	/**
	 * Returns true if this query indicates that matching
	 * properties should be included.
	 */
	public boolean isIncluded() {
		return included;
	}

	@Override
	public boolean equals(final Object o) {
		if (this == o) {
			return true;
		}
		if (o == null || getClass() != o.getClass()) {
			return false;
		}

		PathQuery pathQuery = (PathQuery) o;

		if (included != pathQuery.included) {
			return false;
		}
		if (!Arrays.equals(expression, pathQuery.expression)) {
			return false;
		}

		return true;
	}

	@Override
	public int hashCode() {
		int result = Arrays.hashCode(expression);
		result = 31 * result + (included ? 1 : 0);
		return result;
	}

}


// 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.servlet.filter;

import jodd.io.StreamUtil;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;

/**
 * Implementation of HttpServletResponseWrapper that works with
 * the {@link GzipResponseStream} implementation.
 */
public class GzipResponseWrapper extends HttpServletResponseWrapper {

	/**
	 * Calls the parent constructor which creates a ServletResponse adaptor
	 * wrapping the given response object.
	 */
	public GzipResponseWrapper(final HttpServletResponse response) {
		super(response);
		origResponse = response;

		// explicitly reset content length, as the size of zipped stream is unknown
		response.setContentLength(-1);
	}

	/**
	 * Original response.
	 */
	protected HttpServletResponse origResponse;

	/**
	 * The ServletOutputStream that has been returned by
	 * getOutputStream(), if any.
	 */
	protected ServletOutputStream stream;

	/**
	 * The PrintWriter that has been returned by getWriter(), if any.
	 */
	protected PrintWriter writer;

	/**
	 * The threshold number to compress.
	 */
	protected int threshold;

	/**
	 * Content type.
	 */
	protected String contentType;

	// ---------------------------------------------------------------- public

	/**
	 * Set content type
	 */
	@Override
	public void setContentType(final String contentType) {
		this.contentType = contentType;
		origResponse.setContentType(contentType);
	}

	/**
	 * Set threshold number
	 */
	public void setCompressionThreshold(final int threshold) {
		this.threshold = threshold;
	}

	/**
	 * Creates and returns a ServletOutputStream to write the content associated
	 * with this Response.
	 */
	public ServletOutputStream createOutputStream() throws IOException {
		GzipResponseStream gzstream = new GzipResponseStream(origResponse);
		gzstream.setBuffer(threshold);
		return gzstream;
	}

	/**
	 * Finishes a response.
	 */
	public void finishResponse() {
		StreamUtil.close(writer);
		StreamUtil.close(stream);
	}

	// ---------------------------------------------------------------- ServletResponse

	/**
	 * Flushes the buffer and commit this response.
	 */
	@Override
	public void flushBuffer() throws IOException {
		if (stream != null) {
			stream.flush();
		}
	}

	/**
	 * Returns the servlet output stream associated with this Response.
	 */
	@Override
	public ServletOutputStream getOutputStream() throws IOException {

		if (writer != null) {
			throw new IllegalStateException("getWriter() has already been called for this response");
		}
		if (stream == null) {
			stream = createOutputStream();
		}
		return(stream);
	}

	/**
	 * Returns the writer associated with this Response.
	 */
	@Override
	public PrintWriter getWriter() throws IOException {

		if (writer != null) {
			return writer;
		}

		if (stream != null) {
			throw new IllegalStateException("getOutputStream() has already been called for this response");
		}

		stream = createOutputStream();

		String charEnc = origResponse.getCharacterEncoding();
		if (charEnc != null) {
			writer = new PrintWriter(new OutputStreamWriter(stream, charEnc));
		} else {
			writer = new PrintWriter(stream);
		}
		return(writer);
	}

	/**
	 * Ignores set content length on zipped stream.
	 */
	@Override
	public void setContentLength(final int length) {
	}

	/**
	 * Servlets v3.1 introduce this method, so we need to have it here
	 * in case they are used.
	 * See: https://github.com/oblac/jodd/issues/189
	 */
	@Override
	public void setContentLengthLong(final long length) {
	}

}


// 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.vtor.constraint;

import jodd.vtor.ValidationConstraint;
import jodd.vtor.ValidationConstraintContext;
import jodd.vtor.ValidationContext;

/**
 * Special annotation that validates inner context of provided value.
 */
public class AssertValidConstraint implements ValidationConstraint<AssertValid> {

	protected final ValidationContext targetValidationContext;

	public AssertValidConstraint(final ValidationContext targetValidationContext) {
		this.targetValidationContext = targetValidationContext;
	}

	// ---------------------------------------------------------------- config

	@Override
	public void configure(final AssertValid annotation) {
	}

	// ---------------------------------------------------------------- valid

	/**
	 * Invokes validation on inner context. Always returns true since
	 * inner context violations will be appended to provided validator.
	 */
	@Override
	public boolean isValid(final ValidationConstraintContext vcc, final Object value) {
		if (value == null) {
			return true;
		}
		vcc.validateWithin(targetValidationContext, value);
		return true;
	}
}


// 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.net.URL;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class URLSqlType extends SqlType<URL> {

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

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

}


// 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.oom.sqlgen.chunks;

import jodd.db.oom.DbEntityColumnDescriptor;
import jodd.db.oom.DbEntityDescriptor;
import jodd.db.oom.DbEntityManager;
import jodd.db.oom.sqlgen.DbSqlBuilderException;
import jodd.util.StringPool;

/**
 * Resolves column and table references. Reference is given in format: tableRef.propertyName.
 * The propertyName may be '+' (e.g. tableRef.+), indicating the identity columns.
 * If property name is omitted (e.g. tableRef), only table name will be rendered.
 * If table reference is omitted (e.g. .propertyName), only property i.e. column name
 * will be rendered.
 */
public class ReferenceChunk extends SqlChunk {

	protected final String tableRef;
	protected final String columnRef;
	protected final boolean onlyId;

	public ReferenceChunk(final DbEntityManager dbEntityManager, final String tableRef, final String columnRef) {
		this(dbEntityManager, tableRef, columnRef, false);
	}

	public ReferenceChunk(final DbEntityManager dbEntityManager, final String tableRef, final String columnRef, final boolean onlyId) {
		super(dbEntityManager, CHUNK_REFERENCE);
		this.tableRef = tableRef;
		this.columnRef = columnRef;
		this.onlyId = onlyId;
	}

	public ReferenceChunk(final DbEntityManager dbEntityManager, final String reference) {
		super(dbEntityManager, CHUNK_REFERENCE);

		int dotNdx = reference.indexOf('.');

		if (dotNdx == -1) {
			this.tableRef = reference;
			this.columnRef = null;
			this.onlyId = false;
		} else {
			String ref = reference.substring(0, dotNdx);
			if (ref.length() == 0) {
				ref = null;
			}
			this.tableRef = ref;

			ref = reference.substring(dotNdx + 1);
			if (ref.length() == 0) {
				ref = null;
			}
			this.columnRef = ref;
			onlyId = columnRef != null && columnRef.equals(StringPool.PLUS);
		}
	}

	// ---------------------------------------------------------------- process

	@Override
	public void process(final StringBuilder out) {

		final DbEntityDescriptor ded;

		if (tableRef != null) {
			ded = lookupTableRef(tableRef);

			final String tableName = resolveTable(tableRef, ded);

			out.append(tableName);
		} else {
			ded = findColumnRef(columnRef);
		}

		if (onlyId) {
			if (tableRef != null) {
				out.append('.');
			}
			out.append(ded.getIdColumnName());
		} else if (columnRef != null) {
			DbEntityColumnDescriptor dec = ded.findByPropertyName(columnRef);
			templateData.lastColumnDec = dec;

			if (dec == null) {
				throw new DbSqlBuilderException("Invalid column reference: [" + tableRef + '.' + columnRef + "]");
			}

			if (tableRef != null) {
				out.append('.');
			}
			out.append(dec.getColumnNameForQuery());
		}
	}

}


// 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.oom.sqlgen.chunks;

import jodd.db.oom.ColumnAliasType;
import jodd.db.oom.DbEntityColumnDescriptor;
import jodd.db.oom.DbEntityDescriptor;
import jodd.db.oom.DbEntityManager;
import jodd.db.oom.sqlgen.DbSqlBuilderException;
import jodd.db.oom.sqlgen.TemplateData;
import jodd.util.ArraysUtil;
import jodd.util.StringPool;
import jodd.util.StringUtil;

/**
 * Columns select chunk resolves entity column(s) from column references. Should be used for SELECT queries.
 *

 * Column reference is specified as: {@link TableChunk tableReference}.propertyName where property name is
 * a property of the entity references by table reference. Result is rendered as:
 * tableName.column or alias.column (if table has an alias).
 *

 * There are some special values for propertyName
 *

 *
wildcard (*), all table columns will be listed

 *
id sign (+), all table id columns will be listed

 *

 *

 * If previous chunk is also a column chunk, comma separator will be added in between.
 *

 * Note that column alias are appended to the column name (using 'as' construct).
 *

 * Macro rules:
 *

 *
$C{tableRef} is rendered as FOO.col1, FOO.col2,...

 *
$C{tableRef.*} is equal to above, renders all entity columns

 *
$C{tableRef.+} renders to only identity columns

 *
$C{tableRef.%} renders all but identity columns

 *
$C{tableRef.colRef} is rendered as FOO.column

 *
$C{tableRef.[colRef1,colRef2|...]} is rendered as FOO.column1, FOO.column2,..., support id sign (+)

 *
$C{entityRef.colRef} renders to FOO$column

 *
$C{hint.entityRef...} defines a hint

 *
$C{hint:entityRef...} defines a hint with custom name

 *
$C{.columName} renders as column name

 *
$C{hint:.columName} renders as column name and defines its hint

 *

 */
public class ColumnsSelectChunk extends SqlChunk {

	private static final String AS = " as ";
	private static final char SPLIT = ',';

	protected final String tableRef;
	protected final String columnRef;
	protected final String[] columnRefArr;
	protected final int includeColumns;
	protected final String hint;
	protected final String columnAliasSeparator;

	private ColumnsSelectChunk(
			final DbEntityManager dbEntityManager,
			final String columnAliasSeparator,
			final String tableRef,
			final String columnRef,
			final String[] columnRefArr,
			final int includeColumns,
			final String hint) {
		super(dbEntityManager, CHUNK_SELECT_COLUMNS);
		this.columnAliasSeparator = columnAliasSeparator;
		this.tableRef = tableRef;
		this.columnRef = columnRef;
		this.columnRefArr = columnRefArr;
		this.includeColumns = includeColumns;
		this.hint = hint;
	}

	public ColumnsSelectChunk(final DbEntityManager dbEntityManager, final String columnAliasSeparator, final String tableRef, final String columnRef) {
		this(dbEntityManager, columnAliasSeparator, tableRef, columnRef, null, COLS_NA, null);
	}

	public ColumnsSelectChunk(final DbEntityManager dbEntityManager, final String columnAliasSeparator, final String tableRef, final String... columnRefArr) {
		this(dbEntityManager, columnAliasSeparator, tableRef, null, columnRefArr, COLS_NA_MULTI, null);
	}

	public ColumnsSelectChunk(final DbEntityManager dbEntityManager, final String columnAliasSeparator, final String tableRef, final boolean includeAll) {
		this(dbEntityManager, columnAliasSeparator, tableRef, null, null, includeAll ? COLS_ALL : COLS_ONLY_IDS, null);
	}

	public ColumnsSelectChunk(final DbEntityManager dbEntityManager, final String columnAliasSeparator, String reference) {
		super(dbEntityManager, CHUNK_SELECT_COLUMNS);
		reference = reference.trim();
		int dotNdx = reference.lastIndexOf('.');
		if (dotNdx == -1) {
			this.tableRef = reference;
			this.columnRef = null;
			this.columnRefArr = null;
			this.includeColumns = COLS_ALL;
			this.hint = null;
		} else {

			String tref = reference.substring(0, dotNdx);
			reference = reference.substring(dotNdx + 1);

			// table
			dotNdx = tref.lastIndexOf('.');
			if (dotNdx == -1) {
				this.tableRef = tref;
				this.hint = null;
			} else {
				int doubleColumnNdx = tref.indexOf(':');
				if (doubleColumnNdx == -1) {
					// no special hint
					this.tableRef = tref.substring(dotNdx + 1);
					this.hint = tref;
				} else {
					// hint is different
					this.tableRef = tref.substring(doubleColumnNdx + 1);
					this.hint = tref.substring(0, doubleColumnNdx);
				}
			}

			// column
			if (reference.equals(StringPool.STAR)) {
				this.columnRef = null;
				this.columnRefArr = null;
				this.includeColumns = COLS_ALL;
			} else if (reference.equals(StringPool.PLUS)) {
				this.columnRef = null;
				this.columnRefArr = null;
				this.includeColumns = COLS_ONLY_IDS;
			} else if (reference.equals(StringPool.PERCENT)) {
				this.columnRef = null;
				this.columnRefArr = null;
				this.includeColumns = COLS_ALL_BUT_ID;
			} else if (
					reference.length() != 0
					&& reference.charAt(0) == '['
					&& reference.charAt(reference.length() - 1) == ']') {

				this.columnRef = null;
				this.columnRefArr = StringUtil.splitc(reference.substring(1, reference.length() - 1), SPLIT);
				StringUtil.trimAll(this.columnRefArr);
				this.includeColumns = COLS_NA_MULTI;
			} else {
				this.columnRef = reference;
				this.columnRefArr = null;
				this.includeColumns = COLS_NA;
			}
		}
		this.columnAliasSeparator = columnAliasSeparator;
	}

	// ---------------------------------------------------------------- process

	/**
	 * Counts actual real hints.
	 */
	@Override
	public void init(final TemplateData templateData) {
		super.init(templateData);
		if (hint != null) {
			templateData.incrementHintsCount();
		}
	}

	@Override
	public void process(final StringBuilder out) {
		// hints
		if (templateData.hasHints()) {
			templateData.registerHint(hint == null ? tableRef : hint);
		}

		// columns
		separateByCommaOrSpace(out);

		// special case, only column name, no table ref/name
		if (tableRef.length() == 0) {
			out.append(columnRef);
			return;
		}

		boolean useTableReference = true;
		DbEntityDescriptor ded = lookupTableRef(tableRef, false);
		if (ded == null) {
			useTableReference = false;
			ded = lookupName(tableRef);
		}

		if (columnRef == null) {
			DbEntityColumnDescriptor[] decList = ded.getColumnDescriptors();
			int count = 0;
			boolean withIds = (columnRefArr != null) && ArraysUtil.contains(columnRefArr, StringPool.PLUS);
			for (DbEntityColumnDescriptor dec : decList) {
				if ((includeColumns == COLS_ONLY_IDS) && (!dec.isId())) {
					continue;
				}
				if ((includeColumns == COLS_ALL_BUT_ID) && (dec.isId())) {
					continue;
				}
				if ((includeColumns == COLS_NA_MULTI)
					&& (!withIds || (!dec.isId()))
					&& (!ArraysUtil.contains(columnRefArr, dec.getPropertyName()))) {
					continue;
				}
				if (count > 0) {
					out.append(',').append(' ');
				}
				templateData.lastColumnDec = dec;

				if (useTableReference) {
					appendColumnName(out, ded, dec);
				} else {
					appendAlias(out, ded, dec);
				}
				count++;
			}
		} else {
			final DbEntityColumnDescriptor dec = ded.findByPropertyName(columnRef);
			if (dec == null) {
				throw new DbSqlBuilderException("Invalid column reference: [" + tableRef + '.' + columnRef + "]");
			}

			templateData.lastColumnDec = dec;
			if (useTableReference) {
				appendColumnName(out, ded, dec);
			} else {
				appendAlias(out, ded, dec);
			}
		}
	}

	/**
	 * Appends alias.
	 */
	protected void appendAlias(final StringBuilder query, final DbEntityDescriptor ded, final DbEntityColumnDescriptor dec) {
		final ColumnAliasType columnAliasType = templateData.getColumnAliasType();

		if (columnAliasType == null || columnAliasType == ColumnAliasType.TABLE_REFERENCE) {
			final String tableName = ded.getTableName();
			final String columnName = dec.getColumnNameForQuery();
			templateData.registerColumnDataForTableRef(tableRef, tableName);
			query.append(tableRef).append(columnAliasSeparator).append(columnName);
		} else
		if (columnAliasType == ColumnAliasType.COLUMN_CODE) {
			final String tableName = ded.getTableName();
			final String columnName = dec.getColumnName();
			final String code = templateData.registerColumnDataForColumnCode(tableName, columnName);
			query.append(code);
		} else
		if (columnAliasType == ColumnAliasType.TABLE_NAME) {
			final String tableName = ded.getTableNameForQuery();
			final String columnName = dec.getColumnNameForQuery();
			query.append(tableName).append(columnAliasSeparator).append(columnName);
		}
	}

	/**
	 * Simply appends column name with optional table reference and alias.
	 */
	protected void appendColumnName(final StringBuilder query, final DbEntityDescriptor ded, final DbEntityColumnDescriptor dec) {
		query.append(resolveTable(tableRef, ded)).append('.').append(dec.getColumnName());

		if (templateData.getColumnAliasType() != null) {     // create column aliases

			query.append(AS);

			switch (templateData.getColumnAliasType()) {
				case TABLE_NAME: {
					final String tableName = ded.getTableNameForQuery();
					query.append(tableName).append(columnAliasSeparator).append(dec.getColumnNameForQuery());
					break;
				}
				case TABLE_REFERENCE: {
					final String tableName = ded.getTableName();
					templateData.registerColumnDataForTableRef(tableRef, tableName);
					query.append(tableRef).append(columnAliasSeparator).append(dec.getColumnNameForQuery());
					break;
				}
				case COLUMN_CODE: {
					final String tableName = ded.getTableName();
					final String code = templateData.registerColumnDataForColumnCode(tableName, dec.getColumnName());
					query.append(code);
					break;
				}
			}
		}
	}

}


// 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.inex;

import java.util.ArrayList;
import java.util.List;

/**
 * A single-class rule engine for includes/excludes filtering logic. It can be used when
 * set of objects has to filtered using includes and excludes rules.
 * For example, when filtering files by file name etc.
 *

 * Rule engine works in one of two modes:
 *

 *
blacklist - when any input is allowed by default and when you specify
 *			explicit excludes.
 *

 *
whitelist - when any input is disabled by default and when you specify
 *			explicit includes.
 *

 *

 *

 * The logic of this rule engine depends on the current mode. In both cases,
 * always the inverse rules are considered first. For example, for blacklist
 * mode, engine first examine excludes, and then includes. This way you can
 * set any filter combination.
 *

 * All Jodd classes that filters something uses this class to unify the
 * behavior across the Jodd library.
 *

 * About generics: rule engine examine Values (V). Rules are defined as Definitions (D).
 * They are stored internally as R, that is used with Values.
 */
public class InExRules<V, D, R> implements InExRuleMatcher<V, R> {

	public InExRules<String, String, String> create() {
		return new InExRules<>();
	}

	protected List<Rule<R>> rules;
	protected final InExRuleMatcher<V, R> inExRuleMatcher;
	protected int includesCount;
	protected int excludesCount;
	protected boolean blacklist = true;

	/**
	 * Creates default instance.
	 */
	public InExRules() {
		this.inExRuleMatcher = this;
	}

	/**
	 * Creates instance that uses provided matcher.
	 */
	public InExRules(final InExRuleMatcher<V, R> inExRuleMatcher) {
		this.inExRuleMatcher = inExRuleMatcher;
	}

	/**
	 * Returns total number of all rules.
	 */
	public int totalRules() {
		if (rules == null) {
			return 0;
		}
		return rules.size();
	}

	/**
	 * Returns total number of include rules.
	 */
	public int totalIncludeRules() {
		return includesCount;
	}

	/**
	 * Returns total number of exclude rules.
	 */
	public int totalExcludeRules() {
		return excludesCount;
	}

	/**
	 * Returns true if rule engine has at least one rule set.
	 */
	public boolean hasRules() {
		if (rules == null) {
			return false;
		}
		return !rules.isEmpty();
	}

	/**
	 * Rule definition.
	 */
	public static class Rule<R> {
		public final R value;
		public final boolean include;

		public Rule(final R value, final boolean include) {
			this.value = value;
			this.include = include;
		}

		@Override
		public String toString() {
			return (include ? "+" : "-") + value.toString();
		}

		@Override
		public boolean equals(final Object o) {
			if (this == o) {
				return true;
			}
			if (o == null || getClass() != o.getClass()) {
				return false;
			}

			Rule rule = (Rule) o;

			if (include != rule.include) {
				return false;
			}
			if (!value.equals(rule.value)) {
				return false;
			}

			return true;
		}

		@Override
		public int hashCode() {
			int result = value.hashCode();
			result = 31 * result + (include ? 1 : 0);
			return result;
		}
	}

	/**
	 * Returns rule's value on given index.
	 */
	public R getRule(final int index) {
		return rules.get(index).value;
	}

	/**
	 * Resets all the rules in this rule engine.
	 */
	public void reset() {
		if (rules != null) {
			rules.clear();
		}
		includesCount = excludesCount = 0;
		blacklist = true;
	}

	/**
	 * Enables blacklist mode - everything is included by default,
	 * and user sets explicit excludes.
	 */
	public void blacklist() {
		blacklist = true;
	}

	/**
	 * Returns true if blacklist mode is set.
	 */
	public boolean isBlacklist() {
		return blacklist;
	}

	/**
	 * Enables whitelist mode - everything is excluded by default,
	 * and user set explicit includes.
	 */
	public void whitelist() {
		blacklist = false;
	}

	/**
	 * Returns true if whitelist mode is set.
	 */
	public boolean isWhitelist() {
		return !blacklist;
	}

	/**
	 * Sets blacklist or whitelist mode depending on rules. Smart mode
	 * determines the following:
	 *

	 *
If there are only include rules, then the {@link #whitelist() whitelist} mode is set.

	 *
If there are only excluded rules, then the {@link #blacklist() blacklist} mode is set.

	 *
In any other case (both type of rules exist or no rules are set), then mode is not changed.

	 *

	 * Should be called after all the rules are set, before matching starts.
	 */
	public void detectMode() {
		if (excludesCount == 0 && includesCount > 0) {
			whitelist();
		}
		else if (excludesCount > 0 && includesCount == 0) {
			blacklist();
		}
	}

	/**
	 * Adds include rule.
	 */
	public void include(final D rule) {
		addRule(rule, true);
	}

	/**
	 * Adds exclude rule.
	 */
	public void exclude(final D rule) {
		addRule(rule, false);
	}

	/**
	 * Adds a rule. Duplicates are not allowed and will be ignored.
	 */
	protected void addRule(final D ruleDefinition, final boolean include) {
		if (rules == null) {
			rules = new ArrayList<>();
		}

		if (include) {
			includesCount++;
		} else {
			excludesCount++;
		}

		Rule<R> newRule = new Rule<>(makeRule(ruleDefinition), include);

		if (rules.contains(newRule)) {
			return;
		}

		rules.add(newRule);
	}

	protected R makeRule(final D rule) {
		return (R) rule;
	}

	/**
	 * Matches value against the set of rules using current white/black list mode.
	 */
	public boolean match(final V value) {
		return match(value, blacklist);
	}
	/**
	 * Matches value against the set of rules using provided white/black list mode.
	 */
	public boolean match(final V value, final boolean blacklist) {
		if (rules == null) {
			return blacklist;
		}

		boolean include = blacklist;

		if (include) {
			include = processExcludes(value, true);
			include = processIncludes(value, include);
		}
		else {
			include = processIncludes(value, false);
			include = processExcludes(value, include);
		}

		return include;
	}

	/**
	 * Applies rules on given flag using current black/white list mode.
	 * @see #apply(Object, boolean, boolean)
	 */
	public boolean apply(final V value, final boolean flag) {
		return apply(value, blacklist, flag);
	}

	/**
	 * Applies rules on given flag. Flag is only changed if at least one rule
	 * matched. Otherwise, the same value is returned. This way you can
	 * chain several rules and have the rule engine change the flag
	 * only when a rule is matched.
	 */
	public boolean apply(final V value, final boolean blacklist, boolean flag) {
		if (rules == null) {
			return flag;
		}

		if (blacklist) {
			flag = processExcludes(value, flag);
			flag = processIncludes(value, flag);
		}
		else {
			flag = processIncludes(value, flag);
			flag = processExcludes(value, flag);
		}

		return flag;
	}

	/**
	 * Process includes rules.
	 */
	protected boolean processIncludes(final V value, boolean include) {
		if (includesCount > 0) {
			if (!include) {
				for (Rule<R> rule : rules) {
					if (!rule.include) {
						continue;
					}

					if (inExRuleMatcher.accept(value, rule.value, true)) {
						include = true;
						break;
					}
				}
			}
		}
		return include;
	}

	/**
	 * Process excludes rules.
	 */
	protected boolean processExcludes(final V value, boolean include) {
		if (excludesCount > 0) {
			if (include) {
				for (Rule<R> rule : rules) {
					if (rule.include) {
						continue;
					}

					if (inExRuleMatcher.accept(value, rule.value, false)) {
						include = false;
						break;
					}
				}
			}
		}
		return include;
	}

	/**
	 * Matches value against single rule. By default performs equals on value
	 * against the rule.
	 */
	@Override
	public boolean accept(final V value, final R rule, final boolean include) {
		return value.equals(rule);
	}

}


// 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.csselly.selector;

import jodd.lagarto.dom.Node;

import java.util.List;

/**
 * Pseudo classes.
 */
public abstract class PseudoClass {

	// ---------------------------------------------------------------- STANDARD PSEUDO CLASSES

	/**
	 * Same as :nth-child(1). Represents an element that is the first child of some other element.
	 */
	public static class FIRST_CHILD extends PseudoClass {
		@Override
		public boolean match(final Node node) {
			return node.getSiblingElementIndex() == 0;
		}
	}

	/**
	 * Same as :nth-last-child(1). Represents an element that is the last child of some other element.
	 */
	public static class LAST_CHILD extends PseudoClass {
		@Override
		public boolean match(final Node node) {
			return node.getSiblingElementIndex() == node.getParentNode().getChildElementsCount() - 1;
		}
	}

	/**
	 * Represents an element that has a parent element and whose parent element has no other element children.
	 * Same as :first-child:last-child or :nth-child(1):nth-last-child(1), but with
	 * a lower specificity.
	 */
	public static class ONLY_CHILD extends PseudoClass {
		@Override
		public boolean match(final Node node) {
			return (node.getSiblingElementIndex() == 0) && (node.getParentNode().getChildElementsCount() == 1);
		}
	}

	/**
	 * Same as :nth-of-type(1). Represents an element that is the first sibling of its
	 * type in the list of children of its parent element.
	 */
	public static class FIRST_OF_TYPE extends PseudoClass {
		@Override
		public boolean match(final Node node) {
			return node.getSiblingNameIndex() == 0;
		}
	}

	/**
	 * Same as :nth-last-of-type(1). Represents an element that is the last sibling of its
	 * type in the list of children of its parent element.
	 */
	public static class LAST_OF_TYPE extends PseudoClass {
		@Override
		public boolean match(final Node node) {
			return node.getNextSiblingName() == null;
		}
	}

	/**
	 * Represents an element that is the root of the document.
	 * In HTML 4, this is always the HTML element.
	 */
	public static class ROOT extends PseudoClass {
		@Override
		public boolean match(final Node node) {
			return node.getParentNode().getNodeType() == Node.NodeType.DOCUMENT;
		}
	}

	/**
	 * Represents an element that has no children at all.
	 */
	public static class EMPTY extends PseudoClass {
		@Override
		public boolean match(final Node node) {
			return node.getChildNodesCount() == 0;
		}
	}

	/**
	 * Represents an element that has a parent element and whose parent
	 * element has no other element children with the same expanded element
	 * name. Same as :first-of-type:last-of-type or
	 * :nth-of-type(1):nth-last-of-type(1), but with a lower specificity.
	 */
	public static class ONLY_OF_TYPE extends PseudoClass {
		@Override
		public boolean match(final Node node) {
			return (node.getSiblingNameIndex() == 0) && (node.getNextSiblingName() == null);
		}
	}

	// ---------------------------------------------------------------- EXTENDED PSEUDO CLASSES

	/**
	 * Selects the first matched element.
	 */
	public static class FIRST extends PseudoClass {
		@Override
		public boolean match(final Node node) {
			return true;
		}

		@Override
		public boolean match(final List<Node> currentResults, final Node node, final int index) {
			if (currentResults.isEmpty()) {
				return false;
			}
			Node firstNode = currentResults.get(0);	// getFirst();
			if (firstNode == null) {
				return false;
			}
			return firstNode == node;
		}
	}

	/**
	 * Selects the last matched element. Note that :last selects
	 * a single element by filtering the current collection and matching the
	 * last element within it.
	 */
	public static class LAST extends PseudoClass {
		@Override
		public boolean match(final Node node) {
			return true;
		}

		@Override
		public boolean match(final List<Node> currentResults, final Node node, final int index) {
			int size = currentResults.size();
			if (size == 0) {
				return false;
			}
			Node lastNode = currentResults.get(size - 1); // getLast();
			if (lastNode == null) {
				return false;
			}
			return lastNode == node;
		}
	}

	/**
	 * Selects all button elements and elements of type button.
	 */
	public static class BUTTON extends PseudoClass {
		@Override
		public boolean match(final Node node) {
			String type = node.getAttribute("type");
			if (type == null) {
				return false;
			}
			return type.equals("button");
		}
	}

	/**
	 * Selects all elements of type checkbox.
	 */
	public static class CHECKBOX extends PseudoClass {
		@Override
		public boolean match(final Node node) {
			String type = node.getAttribute("type");
			if (type == null) {
				return false;
			}
			return type.equals("checkbox");
		}
	}

	/**
	 * Selects all elements of type file.
	 */
	public static class FILE extends PseudoClass {
		@Override
		public boolean match(final Node node) {
			String type = node.getAttribute("type");
			if (type == null) {
				return false;
			}
			return type.equals("file");
		}
	}

	/**
	 * Selects all elements that are headers, like h1, h2, h3 and so on.
	 */
	public static class HEADER extends PseudoClass {
		@Override
		public boolean match(final Node node) {
			String name = node.getNodeName();
			if (name == null) {
				return false;
			}
			if (name.length() != 2) {
				return false;
			}
			char c1 = name.charAt(0);
			if (c1 != 'h' && c1 != 'H') {
				return false;
			}
			int c2 = name.charAt(1) - '0';
			return c2 >= 1 && c2 <= 6;
		}
	}

	/**
	 * Selects all elements of type image.
	 */
	public static class IMAGE extends PseudoClass {
		@Override
		public boolean match(final Node node) {
			String type = node.getAttribute("type");
			if (type == null) {
				return false;
			}
			return type.equals("image");
		}
	}

	/**
	 * Selects all input, textarea, select and button elements.
	 */
	public static class INPUT extends PseudoClass {
		@Override
		public boolean match(final Node node) {
			String tagName = node.getNodeName();
			if (tagName == null) {
				return false;
			}
			if (tagName.equals("button")) {
				return true;
			}
			if (tagName.equals("input")) {
				return true;
			}
			if (tagName.equals("select")) {
				return true;
			}
			//noinspection RedundantIfStatement
			if (tagName.equals("textarea")) {
				return true;
			}
			return false;
		}
	}

	/**
	 * Select all elements that are the parent of another element, including text nodes.
	 * This is the inverse of :empty.
	 */
	public static class PARENT extends PseudoClass {
		@Override
		public boolean match(final Node node) {
			return node.getChildNodesCount() != 0;
		}
	}

	/**
	 * Selects all elements of type password.
	 */
	public static class PASSWORD extends PseudoClass {
		@Override
		public boolean match(final Node node) {
			String type = node.getAttribute("type");
			if (type == null) {
				return false;
			}
			return type.equals("password");
		}
	}

	/**
	 * Selects all elements of type radio.
	 */
	public static class RADIO extends PseudoClass {
		@Override
		public boolean match(final Node node) {
			String type = node.getAttribute("type");
			if (type == null) {
				return false;
			}
			return type.equals("radio");
		}
	}

	/**
	 * Selects all elements of type reset.
	 */
	public static class RESET extends PseudoClass {
		@Override
		public boolean match(final Node node) {
			String type = node.getAttribute("type");
			if (type == null) {
				return false;
			}
			return type.equals("reset");
		}
	}

	/**
	 * Selects all elements that are selected.
	 */
	public static class SELECTED extends PseudoClass {
		@Override
		public boolean match(final Node node) {
			return node.hasAttribute("selected");
		}
	}

	/**
	 * Selects all elements that are checked.
	 */
	public static class CHECKED extends PseudoClass {
		@Override
		public boolean match(final Node node) {
			return node.hasAttribute("checked");
		}
	}

	/**
	 * Selects all elements of type submit.
	 */
	public static class SUBMIT extends PseudoClass {
		@Override
		public boolean match(final Node node) {
			String type = node.getAttribute("type");
			if (type == null) {
				return false;
			}
			return type.equals("submit");
		}
	}

	/**
	 * Selects all elements of type text.
	 */
	public static class TEXT extends PseudoClass {
		@Override
		public boolean match(final Node node) {
			String type = node.getAttribute("type");
			if (type == null) {
				return false;
			}
			return type.equals("text");
		}
	}

	/**
	 * Selects even elements, zero-indexed.
	 */
	public static class EVEN extends PseudoClass {
		@Override
		public boolean match(final Node node) {
			return true;
		}

		@Override
		public boolean match(final List<Node> currentResults, final Node node, final int index) {
			return index % 2 == 0;
		}
	}

	/**
	 * Selects odd elements, zero-indexed.
	 */
	public static class ODD extends PseudoClass {
		@Override
		public boolean match(final Node node) {
			return true;
		}

		@Override
		public boolean match(final List<Node> currentResults, final Node node, final int index) {
			return index % 2 != 0;
		}
	}

	// ---------------------------------------------------------------- interface

	/**
	 * Returns true if node matches the pseudoclass.
	 */
	public abstract boolean match(Node node);

	/**
	 * Returns true if node matches the pseudoclass within current results.
	 */
	public boolean match(final List<Node> currentResults, final Node node, final int index) {
		return true;
	}

	/**
	 * Returns pseudo-class name from simple class name.
	 */
	public String getPseudoClassName() {
		String name = getClass().getSimpleName().toLowerCase();
		name = name.replace('_', '-');
		return name;
	}

}


// 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.collection;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;

/**
 * An extension of ArrayList that insures that all of the items
 * added are sorted. This breaks original list contract!.
 * A binary search method is used to provide a quick way to
 * auto sort this list.Note: Not all methods for adding and
 * removing elements are supported.
 */
public class SortedArrayList<E> extends ArrayList<E> {

	protected final Comparator<E> comparator;

	/**
	 * Constructs a new SortedArrayList.
	 */
	public SortedArrayList(final Comparator<E> c) {
		comparator = c;
	}

	/**
	 * Constructs a new SortedArrayList expecting
	 * elements are comparable.
	 */
	public SortedArrayList() {
		comparator = null;
	}

	/**
	 * Constructs a new SortedArrayList expecting
	 * elements are comparable.
	 */
	public SortedArrayList(final Collection<? extends E> c) {
		comparator = null;
		addAll(c);
	}

	/**
	 * Returns comparator assigned to this collection, if such exist.
	 */
	public Comparator getComparator() {
		return comparator;
	}

	// ---------------------------------------------------------------- override

	/**
	 * Adds an Object to sorted list. Object is inserted at correct place, found
	 * using binary search. If the same item exist, it will be put to the end of
	 * the range.
	 *

	 * This method breaks original list contract since objects are not
	 * added at the list end, but in sorted manner.
	 */
	@Override
	public boolean add(final E o) {
		int idx = 0;
		if (!isEmpty()) {
			idx = findInsertionPoint(o);
		}
		super.add(idx, o);
		return true;
	}

	/**
	 * Add all of the elements in the given collection to this list.
	 */
	@Override
	public boolean addAll(final Collection<? extends E> c) {
		Iterator<? extends E> i = c.iterator();
		boolean changed = false;
		while (i.hasNext()) {
			boolean ret = add(i.next());
			if (!changed) {
				changed = ret;
			}
		}
		return changed;
	}

	/**
	 * Finds the index at which object should be inserted.
	 */
	public int findInsertionPoint(final E o) {
		return findInsertionPoint(o, 0, size() - 1);
	}

	// ---------------------------------------------------------------- unsupported methods

	/**
	 * @throws UnsupportedOperationException This method not supported.
	 */
	@Override
	public void add(final int index, final E element) {
		throw new UnsupportedOperationException();
	}

	/**
	 * @throws UnsupportedOperationException This method not supported.
	 */
	@Override
	public E set(final int index, final E element) {
		throw new UnsupportedOperationException();
	}

	/**
	 * @throws UnsupportedOperationException This method not supported.
	 */
	@Override
	public boolean addAll(final int index, final Collection<? extends E> c) {
		throw new UnsupportedOperationException();
	}

	// ---------------------------------------------------------------- sorting

	/**
	 * Compares two keys using the correct comparison method for this
	 * collection.
	 */
	@SuppressWarnings( {"unchecked"})
	protected int compare(final E k1, final E k2) {
		if (comparator == null) {
			return ((Comparable) k1).compareTo(k2);
		}
		return comparator.compare(k1, k2);
	}

	/**
	 * Conducts a binary search to find the index where Object
	 * should be inserted.
	 */
	protected int findInsertionPoint(final E o, int low, int high) {

		while (low <= high) {
			int mid = (low + high) >>> 1;
			int delta = compare(get(mid), o);

			if (delta > 0) {
				high = mid - 1;
			} else {
				low = mid + 1;
			}
		}

		return low;
	}

}


// 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.log.impl;

import jodd.log.Logger;
import jodd.log.LoggerProvider;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.spi.AbstractLogger;

/**
 * Log4j2 logger.
 */
public class Log4j2Logger implements Logger {

	public static final LoggerProvider<Log4j2Logger> PROVIDER =
		name -> new Log4j2Logger(LogManager.getLogger(name));

	private static final String FQCN = Log4j2Logger.class.getName();

	final org.apache.logging.log4j.Logger logger;
	private final AbstractLogger abstractLogger;

	public Log4j2Logger(final org.apache.logging.log4j.Logger logger) {
		this.logger = logger;
		if (logger instanceof AbstractLogger) {
			abstractLogger = (AbstractLogger) logger;
		}
		else {
			abstractLogger = null;
		}
	}

	/**
	 * Converts Jodd logging level to JDK.
	 */
	private org.apache.logging.log4j.Level jodd2log4j2(final Logger.Level level) {
		switch (level) {
			case TRACE: return org.apache.logging.log4j.Level.TRACE;
			case DEBUG: return org.apache.logging.log4j.Level.DEBUG;
			case INFO:	return org.apache.logging.log4j.Level.INFO;
			case WARN:	return org.apache.logging.log4j.Level.WARN;
			case ERROR:	return org.apache.logging.log4j.Level.ERROR;
			default:
				throw new IllegalArgumentException();
		}
	}

	@Override
	public String getName() {
		return logger.getName();
	}

	@Override
	public boolean isEnabled(final Logger.Level level) {
		return logger.isEnabled(jodd2log4j2(level));
	}

	@Override
	public void log(final Logger.Level level, final String message) {
		if (abstractLogger != null) {
			abstractLogger.logIfEnabled(FQCN, jodd2log4j2(level), null, message);
		}
		else {
			logger.log(jodd2log4j2(level), message);
		}
	}
	@Override
	public void log(final Logger.Level level, final String message, final Throwable throwable) {
		if (abstractLogger != null) {
			abstractLogger.logIfEnabled(FQCN, jodd2log4j2(level), null, message, throwable);
		}
		else {
			logger.log(jodd2log4j2(level), message, throwable);
		}
	}

	@Override
	public void setLevel(final Logger.Level level) {
		throw new UnsupportedOperationException();
	}

	@Override
	public boolean isTraceEnabled() {
		return logger.isTraceEnabled();
	}

	@Override
	public void trace(final String message) {
		if (abstractLogger != null) {
			abstractLogger.logIfEnabled(FQCN, org.apache.logging.log4j.Level.TRACE, null, message);
		}
		else {
			logger.trace(message);
		}
	}

	@Override
	public boolean isDebugEnabled() {
		return logger.isDebugEnabled();
	}

	@Override
	public void debug(final String message) {
		if (abstractLogger != null) {
			abstractLogger.logIfEnabled(FQCN, org.apache.logging.log4j.Level.DEBUG, null, message);
		}
		else {
			logger.debug(message);
		}
	}

	@Override
	public boolean isInfoEnabled() {
		return logger.isInfoEnabled();
	}

	@Override
	public void info(final String message) {
		if (abstractLogger != null) {
			abstractLogger.logIfEnabled(FQCN, org.apache.logging.log4j.Level.INFO, null, message);
		}
		else {
			logger.info(message);
		}
	}

	@Override
	public boolean isWarnEnabled() {
		return logger.isWarnEnabled();
	}

	@Override
	public void warn(final String message) {
		if (abstractLogger != null) {
			abstractLogger.logIfEnabled(FQCN, org.apache.logging.log4j.Level.WARN, null, message);
		}
		else {
			logger.warn(message);
		}
	}

	@Override
	public void warn(final String message, final Throwable throwable) {
		if (abstractLogger != null) {
			abstractLogger.logIfEnabled(FQCN, org.apache.logging.log4j.Level.WARN, null, message, throwable);
		}
		else {
			logger.warn(message, throwable);
		}
	}

	@Override
	public boolean isErrorEnabled() {
		return logger.isErrorEnabled();
	}

	@Override
	public void error(final String message) {
		if (abstractLogger != null) {
			abstractLogger.logIfEnabled(FQCN, org.apache.logging.log4j.Level.ERROR, null, message);
		}
		else {
			logger.error(message);
		}
	}

	@Override
	public void error(final String message, final Throwable throwable) {
		if (abstractLogger != null) {
			abstractLogger.logIfEnabled(FQCN, org.apache.logging.log4j.Level.ERROR, null, message, throwable);
		}
		else {
			logger.error(message, throwable);
		}
	}

}


// 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.csselly.selector;

import jodd.csselly.Selector;
import jodd.lagarto.dom.Node;
import jodd.lagarto.dom.NodeFilter;

/**
 * Attribute selector.
 */
public class AttributeSelector extends Selector implements NodeFilter {

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

	protected final String name;
	protected final String value;
	protected final Match match;
	protected char quoteChar;

	public AttributeSelector(final String name, final String sign, final String value) {
		super(Type.ATTRIBUTE);
		this.name = name.trim();
		this.value = extractValue(value);
		this.match = Match.valueOf(sign);
	}

	public AttributeSelector(final String name, final Match match, final String value) {
		super(Type.ATTRIBUTE);
		this.name = name.trim();
		this.match = match;
		this.value = extractValue(value);
	}

	public AttributeSelector(final String attr) {
		super(Type.ATTRIBUTE);
		int index = attr.indexOf('=');
		if (index == -1) {
			this.name = attr.trim();
			this.match = null;
			this.value = null;
			return;
		}
		char first = attr.charAt(index - 1);
		this.match = Match.valueOfFirstChar(first);

		int signLen = this.match.getSign().length();
		index -= (signLen - 1);
		this.name = attr.substring(0, index).trim();
		this.value = extractValue(attr.substring(index + signLen));
	}

	protected String extractValue(String value) {
		quoteChar = value.charAt(0);
		if (quoteChar != '"' && quoteChar != '\'') {
			quoteChar = 0;
		}

		if (quoteChar != 0) {
			value = value.substring(1, value.length() - 1);
		}
		return value.trim();
	}

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

	/**
	 * Returns attribute name.
	 */
	public String getName() {
		return name;
	}

	/**
	 * Returns attribute value or null if doesn't exist.
	 */
	public String getValue() {
		return value;
	}

	/**
	 * Returns matching type.
	 */
	public Match getMatch() {
		return match;
	}

	/**
	 * Returns the quote char or 0 if quote is not used.
	 */
	public char getQuoteChar() {
		return quoteChar;
	}

	// ---------------------------------------------------------------- match

	public boolean accept(final Node node) {
		if (!node.hasAttribute(name)) {
			return false;
		}

		if (value == null) {		// just detect if attribute exist
			return true;
		}

		String nodeValue = node.getAttribute(name);

		if (nodeValue == null) {
			return false;
		}

		return match.compare(nodeValue, value);
	}
}


// 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.madvoc.ActionConfig;
import jodd.madvoc.MadvocException;
import jodd.madvoc.meta.Action;
import jodd.madvoc.meta.ActionAnnotationValues;
import jodd.madvoc.meta.ActionConfiguredBy;
import jodd.petite.meta.PetiteInject;
import jodd.util.ArraysUtil;
import jodd.util.annotation.AnnotationParser;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.util.function.Consumer;

/**
 * Manager for action configurations and action annotation.
 */
public class ActionConfigManager {

	private final TypeCache<ActionConfig> actionConfigs = TypeCache.createDefault();
	private AnnotationParser[] annotationParsers = new AnnotationParser[0];

	@PetiteInject
	protected ContextInjectorComponent contextInjectorComponent;

	/**
	 * Registers action configuration for given annotation. New {@link ActionConfig} is created
	 * and stored.
	 */
	public void registerAnnotation(final Class<? extends Annotation> annotationType) {
		final ActionConfiguredBy actionConfiguredBy = annotationType.getAnnotation(ActionConfiguredBy.class);

		if (actionConfiguredBy == null) {
			throw new MadvocException("Action annotation is missing it's " + ActionConfiguredBy.class.getSimpleName() + " configuration.");
		}

		bindAnnotationConfig(annotationType, actionConfiguredBy.value());
	}

	/**
	 * Binds action annotation and the action config. This can overwrite the default annotation
	 * configuration of an annotation.
	 */
	public void bindAnnotationConfig(final Class<? extends Annotation> annotationType, final Class<? extends ActionConfig> actionConfigClass) {
		final ActionConfig actionConfig = registerNewActionConfiguration(actionConfigClass);

		actionConfigs.put(annotationType, actionConfig);

		for (final AnnotationParser annotationParser : annotationParsers) {
			if (annotationType.equals(annotationParser.getAnnotationType())) {
				// parser already exists
				return;
			}
		}
		annotationParsers = ArraysUtil.append(annotationParsers, new AnnotationParser(annotationType, Action.class));
	}

	/**
	 * Registers action configuration for given type.
	 */
	protected ActionConfig registerNewActionConfiguration(final Class<? extends ActionConfig> actionConfigClass) {
		final ActionConfig newActionConfig = createActionConfig(actionConfigClass);

		actionConfigs.put(actionConfigClass, newActionConfig);

		return newActionConfig;
	}

	/**
	 * Lookup for the action configuration. Typically, the input argument is either the action type or annotation type.
	 */
	public ActionConfig lookup(final Class actionTypeOrAnnotationType) {
		final ActionConfig actionConfig = actionConfigs.get(actionTypeOrAnnotationType);

		if (actionConfig == null) {
			throw new MadvocException("ActionConfiguration not registered:" + actionTypeOrAnnotationType.getName());
		}
		return actionConfig;
	}

	/**
	 * Fetch some action config and consumes it.
	 */
	public <T extends ActionConfig> void with(final Class<T> actionConfigType, final Consumer<T> actionConfigConsumer) {
		final T actionConfig = (T) lookup(actionConfigType);
		actionConfigConsumer.accept(actionConfig);
	}

	/**
	 * Returns {@code true} if annotated element as action annotation on it.
	 */
	public boolean hasActionAnnotationOn(final AnnotatedElement annotatedElement) {
		for (final AnnotationParser annotationParser : annotationParsers) {
			if (annotationParser.hasAnnotationOn(annotatedElement)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Reads annotation value from annotated element. If annotation does not exist, returns {@code null}.
	 */
	public ActionAnnotationValues readAnnotationValue(final AnnotatedElement annotatedElement) {
		for (final AnnotationParser annotationParser : annotationParsers) {
			if (annotationParser.hasAnnotationOn(annotatedElement)) {
				return ActionAnnotationValues.of(annotationParser, annotatedElement);
			}
		}
		return null;
	}

	protected ActionConfig createActionConfig(final Class<? extends ActionConfig> actionConfigClass) {
		try {
			final Constructor<? extends ActionConfig> ctor = actionConfigClass.getDeclaredConstructor();
			final ActionConfig actionConfig = ctor.newInstance();

			contextInjectorComponent.injectContext(actionConfig);

			return actionConfig;
		}
		catch (Exception ex) {
			throw new MadvocException("Invalid action configuration class: " + actionConfigClass.getSimpleName(), 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.mail;

import javax.mail.MessagingException;
import javax.mail.Service;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.Transport;
import javax.mail.internet.MimeUtility;

/**
 * Mail session base.
 */
abstract class MailSession<T extends Service> implements AutoCloseable {

	public static class Defaults {
		/**
		 * If set to {@code true}, the setFileName method uses the
		 * {@link MimeUtility#encodeText(String)} to encode any non-ASCII characters in the filename.
		 * Note that this encoding violates the MIME specification, but is useful for interoperating
		 * with some mail clients that use this convention. The default is {@code true}.
		 */
		public static boolean mailMimeEncodefilename = true;
		/**
		 * If set to {@code true}, the setFileName method uses the
		 * {@link MimeUtility#encodeText(String)} to encode any non-ASCII characters in the filename.
		 * Note that this encoding violates the MIME specification, but is useful for interoperating
		 * with some mail clients that use this convention. The default is {@code true}.
		 */
		public static boolean mailMimeDecodefilename = true;
	}

	/**
	 * Setups the system email properties.
	 */
	protected static void setupSystemMailProperties() {
		System.setProperty("mail.mime.encodefilename", Boolean.valueOf(Defaults.mailMimeEncodefilename).toString());
		System.setProperty("mail.mime.decodefilename", Boolean.valueOf(Defaults.mailMimeDecodefilename).toString());
	}

	private final Session session;
	protected final Service service;

	/**
	 * Creates new mail session.
	 *
	 * @param session {@link Session}.
	 * @param service {@link Service} such as {@link Store} or {@link Transport}.
	 */
	protected MailSession(final Session session, final Service service) {
		this.session = session;
		this.service = service;
	}

	/**
	 * Opens session.
	 */
	public void open() {
		try {
			service.connect();
		} catch (final MessagingException msex) {
			throw new MailException("Open session error", msex);
		}
	}

	/**
	 * Closes session.
	 */
	@Override
	public void close() {
		try {
			service.close();
		} catch (final MessagingException mex) {
			throw new MailException("Failed to close session", mex);
		}
	}

	/**
	 * Returns {@code true} if mail session is still connected.
	 *
	 * @return {@code true} if mail session is still connected.
	 */
	public boolean isConnected() {
		return service.isConnected();
	}

	/**
	 * Returns the {@link Session}.
	 *
	 * @return the {@link Session}.
	 */
	public Session getSession() {
		return session;
	}

	/**
	 * Returns the {@link Service}.
	 *
	 * @return the {@link Service}.
	 */
	public abstract T getService();
}


// 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.vtor;

/**
 * Validation constraint context, used for validation in {@link ValidationConstraint}.
 */
public class ValidationConstraintContext {

	protected final Vtor vtor;
	protected final Object target;
	protected final String name;

	public ValidationConstraintContext(final Vtor vtor, final Object target, final String name) {
		this.vtor = vtor;
		this.target = target;
		this.name = name;
	}

	/**
	 * Returns validator.
	 */
	public Vtor getValidator() {
		return vtor;
	}

	/**
	 * Returns target object containing the value.
	 */
	public Object getTarget() {
		return target;
	}

	/**
	 * Returns context name.
	 */
	public String getName() {
		return name;
	}

	/**
	 * Validates provided context and value withing this constraint content.
	 */
	public void validateWithin(final ValidationContext vctx, final Object value) {
		vtor.validate(vctx, value, name);
	}
}