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

import jodd.log.Logger;
import jodd.log.LoggerFactory;

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

import static jodd.jtx.JtxStatus.STATUS_ACTIVE;
import static jodd.jtx.JtxStatus.STATUS_COMMITTED;
import static jodd.jtx.JtxStatus.STATUS_COMMITTING;
import static jodd.jtx.JtxStatus.STATUS_MARKED_ROLLBACK;
import static jodd.jtx.JtxStatus.STATUS_NO_TRANSACTION;
import static jodd.jtx.JtxStatus.STATUS_ROLLEDBACK;
import static jodd.jtx.JtxStatus.STATUS_ROLLING_BACK;
import static jodd.jtx.JtxStatus.STATUS_UNKNOWN;
import static jodd.jtx.JtxTransactionMode.DEFAULT_TIMEOUT;

/**
 * Transaction is an unit of work that is performed by one or more resources.
 * Created and controlled by {@link JtxTransactionManager transaction manager}.
 *

 * Transaction is associated to a thread from where it was created.
 * Transaction may have more than one resource attached.
 *

 * Committing and rolling back transaction actually performs commit/rollback on
 * all attached resources. Therefore, it may happens that committing of one resource
 * fails, what actually breaks the atomicity of the transaction. If transaction is
 * attached just to one resource, atomicity is saved.
 *

 * Only one resource of some type may exists in the transaction. There is no way to have
 * two resources of the same type in one transaction.
 *

 * This class is responsible for transaction life-cycle. It can be consider that all
 * work is scoped to associated thread.
 */
public class JtxTransaction {

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

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

	protected final JtxTransactionManager txManager;
	protected final JtxTransactionMode mode;
	protected final Set<JtxResource> resources;
	protected final Object scope;
	protected final long deadline;
	protected final boolean startAsActive;
	protected Throwable rollbackCause;
	protected JtxStatus status;

	/**
	 * Creates new transaction. Should be invoked by {@link jodd.jtx.JtxTransactionManager}.
	 * If transaction is set as active, it will be actually created, meaning
	 * that it is the first transaction on this connection i.e. in this session.
	 * If transaction is not active, transaction object will be created,
	 * but the real transaction not, and it is expected that one is already created before.
	 *
	 * @param txManager jtx manager
	 * @param mode transaction mode
	 * @param scope transaction live scope within the other transaction requests are ignored
	 * @param active if true it is an active transaction, otherwise it's not
	 */
	public JtxTransaction(final JtxTransactionManager txManager, final JtxTransactionMode mode, final Object scope, final boolean active) {
		this.txManager = txManager;
		this.mode = mode;
		this.scope = scope;
		this.resources = new HashSet<>();
		this.deadline = mode.getTransactionTimeout() == DEFAULT_TIMEOUT ?
				DEFAULT_TIMEOUT :
				System.currentTimeMillis() + (mode.getTransactionTimeout() * 1000L);
		this.status = active ? STATUS_ACTIVE : STATUS_NO_TRANSACTION;
		this.startAsActive = active;
		txManager.associateTransaction(this);
		if (log.isDebugEnabled()) {
			log.debug("New JTX {status:" + this.status + ", mode:" + this.mode + '}');
		}
	}

	/**
	 * Returns transaction mode.
	 */
	public JtxTransactionMode getTransactionMode() {
		return mode;
	}

	/**
	 * Returns transaction manager that owns this transaction.
	 */
	public JtxTransactionManager getTransactionManager() {
		return txManager;
	}

	/**
	 * Returns transaction scope if exist, or null.
	 */
	public Object getScope() {
		return scope;
	}

	// ---------------------------------------------------------------- status

	/**
	 * Returns current transaction status.
	 */
	public JtxStatus getStatus() {
		return status;
	}

	/**
	 * Returns true if transaction started as active one.
	 * This value is never changed, while {@link #getStatus() status}
	 * changes during the execution.
	 */
	public boolean isStartAsActive() {
		return startAsActive;
	}

	/**
	 * Returns true if transaction is active.
	 * This status changes during the transaction flow.
	 */
	public boolean isActive() {
		return status == STATUS_ACTIVE;
	}

	/**
	 * Returns true if transaction is explicitly forbidden, i.e.
	 * session is in auto-commit mode.
	 */
	public boolean isNoTransaction() {
		return status == STATUS_NO_TRANSACTION;
	}

	/**
	 * Returns true if transaction and all its resources are committed successfully.
	 */
	public boolean isCommitted() {
		return status == STATUS_COMMITTED;
	}

	/**
	 * Returns true if transaction and all its resources are rolled-back successfully.
	 */
	public boolean isRolledback() {
		return status == STATUS_ROLLEDBACK;
	}

	/**
	 * Returns true if transaction is either committed or rolled back.
	 */
	public boolean isCompleted() {
		return status == STATUS_COMMITTED || status == STATUS_ROLLEDBACK;
	}

	// ---------------------------------------------------------------- rollback

	/**
	 * Modify the transaction associated with the target object such that the only possible outcome
	 * of the transaction is to roll back the transaction.
	 */
	public void setRollbackOnly() {
		setRollbackOnly(null);
	}

	/**
	 * Modify the transaction associated with the target object such that the only possible outcome
	 * of the transaction is to roll back the transaction.
	 */
	public void setRollbackOnly(final Throwable th) {
		if (!isNoTransaction()) {
			if ((status != STATUS_MARKED_ROLLBACK) && (status != STATUS_ACTIVE)) {
				throw new JtxException("TNo active TX that can be marked as rollback only");
			}
		}
		rollbackCause = th;
		status = STATUS_MARKED_ROLLBACK;
	}

	/**
	 * Returns true if transaction is marked as rollback only.
	 */
	public boolean isRollbackOnly() {
		return status == STATUS_MARKED_ROLLBACK;
	}

	// ---------------------------------------------------------------- timeout

	/**
	 * Sets the rollback-only if the deadline has been reached and throws an exception.
	 */
	protected void checkTimeout() {
		if (deadline == DEFAULT_TIMEOUT) {
			return;
		}
		if (this.deadline - System.currentTimeMillis() < 0) {
			setRollbackOnly();
			throw new JtxException("TX timed out, marked as rollback only");
		}
	}

	// ---------------------------------------------------------------- core

	/**
	 * Commit and completes current transaction. Transaction is committed on all attached resources. After, resources
	 * are detached from the transaction. When this method completes,transaction is no longer
	 * associated with current thread.
	 */
	public void commit() {
		checkTimeout();
		commitOrRollback(true);
	}

	/**
	 * Roll back and completes current transaction. Transaction is rolled back on all attached resources.
	 * Resource are then detached from the transaction. When this method completes, transaction is no
	 * longer associated with current thread.
	 */
	public void rollback() {
		commitOrRollback(false);
	}

	/**
	 * Performs either commit or rollback on all transaction resources.
	 */
	protected void commitOrRollback(boolean doCommit) {
		if (log.isDebugEnabled()) {
			if (doCommit) {
				log.debug("Commit JTX");
			} else {
				log.debug("Rollback JTX");
			}
		}
		boolean forcedRollback = false;
		if (!isNoTransaction()) {
			if (isRollbackOnly()) {
				if (doCommit) {
					doCommit = false;
					forcedRollback = true;
				}
			} else if (!isActive()) {
				if (isCompleted()) {
					throw new JtxException("TX is already completed, commit or rollback should be called once per TX");
				}
				throw new JtxException("No active TX to " + (doCommit ? "commit" : "rollback"));
			}
		}
		if (doCommit) {
			commitAllResources();
		} else {
			rollbackAllResources(forcedRollback);
		}
	}

	// ---------------------------------------------------------------- resources

	/**
	 * Commits all attached resources. On successful commit, resource will be closed
	 * and detached from this transaction. On exception, resource remains attached
	 * to transaction.
	 *

	 * All resources will be committed, even if commit fails on some in that process.
	 * If there was at least one failed commit, its exception will be re-thrown after finishing
	 * committing all resources, and transaction will be marked as rollback only.
	 */
	protected void commitAllResources() throws JtxException {
		status = STATUS_COMMITTING;
		Exception lastException = null;
		Iterator<JtxResource> it = resources.iterator();
		while (it.hasNext()) {
			JtxResource resource = it.next();
			try {
				resource.commitTransaction();
				it.remove();
			} catch (Exception ex) {
				lastException = ex;
			}
		}
		if (lastException != null) {
			setRollbackOnly(lastException);
			throw new JtxException("Commit failed: one or more TX resources couldn't commit a TX", lastException);
		}
		txManager.removeTransaction(this);
		status = STATUS_COMMITTED;
	}

	/**
	 * Rollbacks all attached resources. Resource will be closed. and detached from this transaction.
	 * If exception occurs, it will be rethrown at the end.
	 */
	protected void rollbackAllResources(final boolean wasForced) {
		status = STATUS_ROLLING_BACK;
		Exception lastException = null;
		Iterator<JtxResource> it = resources.iterator();
		while (it.hasNext()) {
			JtxResource resource = it.next();
			try {
				resource.rollbackTransaction();
			} catch (Exception ex) {
				lastException = ex;
			} finally {
				it.remove();
			}
		}
		txManager.removeTransaction(this);
		status = STATUS_ROLLEDBACK;
		if (lastException != null) {
			status = STATUS_UNKNOWN;
			throw new JtxException("Rollback failed: one or more TX resources couldn't rollback a TX", lastException);
		}
		if (wasForced) {
			throw new JtxException("TX rolled back because it has been marked as rollback-only", rollbackCause);
		}
	}

	// ---------------------------------------------------------------- resources

	/**
	 * Requests a resource. If resource is not found, it will be created and new transaction will be started on it.
	 */
	public <E> E requestResource(final Class<E> resourceType) {
		if (isCompleted()) {
			throw new JtxException("TX is already completed, resource are not available after commit or rollback");
		}
		if (isRollbackOnly()) {
			throw new JtxException("TX is marked as rollback only, resource are not available", rollbackCause);
		}
		if (!isNoTransaction() && !isActive()) {
			throw new JtxException("Resources are not available since TX is not active");
		}
		checkTimeout();
		E resource = lookupResource(resourceType);
		if (resource == null) {
			int maxResources = txManager.getMaxResourcesPerTransaction();
			if ((maxResources != -1) && (resources.size() >= maxResources)) {
				throw new JtxException("TX already has attached max. number of resources");
			}
			JtxResourceManager<E> resourceManager = txManager.lookupResourceManager(resourceType);
			resource = resourceManager.beginTransaction(mode, isActive());
			resources.add(new JtxResource<>(this, resourceManager, resource));
		}
		return resource;
	}

	/**
	 * Lookups for open resource. Returns null if resource not found.
	 * Only open resources can be found.
	 */
	protected <E> E lookupResource(final Class<E> resourceType) {
		for (JtxResource jtxResource : resources) {
			if (jtxResource.isSameTypeAsResource(resourceType)) {
				//noinspection unchecked
				return (E) jtxResource.getResource();
			}
		}
		return null;
	}
}


// 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.CharUtil;
import jodd.util.StringPool;

import java.io.IOException;

import static jodd.util.StringPool.NULL;

/**
 * Simple JSON writer.
 */
public class JsonWriter {

	protected final Appendable out;
	protected final boolean strictStringEncoding;

	public JsonWriter(final Appendable out, final boolean strictStringEncoding) {
		this.out = out;
		this.strictStringEncoding = strictStringEncoding;
	}

	// ---------------------------------------------------------------- push

	protected String pushedName;
	protected boolean pushedComma;
	protected boolean isPushed;

	/**
	 * Stores name to temporary stack. Used when name's value may or may not be
	 * serialized (e.g. it may be excluded), in that case we do not need to
	 * write the name.
	 */
	public void pushName(final String name, final boolean withComma) {
		pushedName = name;
		pushedComma = withComma;
		isPushed = true;
	}

	/**
	 * Writes stored name to JSON string. Cleans storage.
	 */
	protected void popName() {
		if (isPushed) {
			if (pushedComma) {
				writeComma();
			}
			String name = pushedName;
			pushedName = null;
			isPushed = false;
			writeName(name);
		}
	}

	/**
	 * Returns true if {@link #pushName(String, boolean)}  pushed name}
	 * has been {@link #popName() poped, i.e. used}.
	 */
	public boolean isNamePopped() {
		boolean b = !isPushed;
		isPushed = false;
		return b;
	}

	// ---------------------------------------------------------------- write

	/**
	 * Writes open object sign.
	 */
	public void writeOpenObject() {
		popName();
		write('{');
	}

	/**
	 * Writes close object sign.
	 */
	public void writeCloseObject() {
		write('}');
	}

	/**
	 * Writes object's property name: string and a colon.
	 */
	public void writeName(final String name) {
		if (name != null) {
			writeString(name);
		}
		else {
			write(NULL);
		}

		write(':');
	}

	/**
	 * Writes open array sign.
	 */
	public void writeOpenArray() {
		popName();
		write('[');
	}

	/**
	 * Writes close array sign.
	 */
	public void writeCloseArray() {
		write(']');
	}

	/**
	 * Write a quoted and escaped value to the output.
	 */
	public void writeString(final String value) {
		popName();

		write(StringPool.QUOTE);

		int len = value.length();

		for (int i = 0; i < len; i++) {
			char c = value.charAt(i);

			switch (c) {
				case '"':
					write("\\\"");
					break;
				case '\\':
					write("\\\\");
					break;
				case '/':
					if (strictStringEncoding) {
						write("\\/");
					}
					else {
						write(c);
					}
					break;
				case '\b':
					write("\\b");
					break;
				case '\f':
					write("\\f");
					break;
				case '\n':
					write("\\n");
					break;
				case '\r':
					write("\\r");
					break;
				case '\t':
					write("\\t");
					break;
				default:
					if (Character.isISOControl(c)) {
						unicode(c);
					}
					else {
						write(c);
					}
			}
		}

		write(StringPool.QUOTE);
	}

	/**
	 * Writes unicode representation of a character.
	 */
	protected void unicode(final char c) {
		write("\\u");
		int n = c;
		for (int i = 0; i < 4; ++i) {
			int digit = (n & 0xf000) >> 12;
			char hex = CharUtil.int2hex(digit);

			write(hex);

			n <<= 4;
		}
	}

	/**
	 * Writes comma.
	 */
	public void writeComma() {
		write(',');
	}

	/**
	 * Appends char sequence to the buffer. Used for numbers, nulls, booleans, etc.
	 */
	public void write(final CharSequence charSequence) {
		popName();
		try {
			out.append(charSequence);
		} catch (IOException ioex) {
			throw new JsonException(ioex);
		}
	}

	public void writeNumber(final Number number) {
		if (number == null) {
			write(StringPool.NULL);
			return;
		}
		write(number.toString());
	}

	/**
	 * Appends char to the buffer. Used internally.
	 */
	protected void write(final char c) {
		try {
			out.append(c);
		} catch (IOException ioex) {
			throw new JsonException(ioex);
		}
	}

}


// 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.lagarto.dom;

import jodd.lagarto.LagartoParserConfig;
import jodd.log.Logger;

/**
 * Additional configuration for {@link jodd.lagarto.dom.LagartoDOMBuilder}
 * based on {@link jodd.lagarto.LagartoParserConfig}.
 */
public class LagartoDomBuilderConfig extends LagartoParserConfig<LagartoDomBuilderConfig> {

	protected boolean ignoreWhitespacesBetweenTags;
	protected boolean ignoreComments;
	protected boolean selfCloseVoidTags;
	protected boolean collectErrors;
	protected float condCommentIEVersion = 10;
	protected boolean enabledVoidTags = true;
	protected boolean impliedEndTags;
	protected Logger.Level parsingErrorLogLevel = Logger.Level.WARN;

	protected boolean useFosterRules;
	protected boolean unclosedTagAsOrphanCheck;

	protected LagartoHtmlRenderer lagartoHtmlRenderer = new LagartoHtmlRenderer();

	// ---------------------------------------------------------------- access

	public boolean isUnclosedTagAsOrphanCheck() {
		return unclosedTagAsOrphanCheck;
	}

	public LagartoDomBuilderConfig setUnclosedTagAsOrphanCheck(final boolean unclosedTagAsOrphanCheck) {
		this.unclosedTagAsOrphanCheck = unclosedTagAsOrphanCheck;
		return this;
	}

	/**
	 * Returns true if {@link HtmlFosterRules foster rules}
	 * should be used.
	 */
	public boolean isUseFosterRules() {
		return useFosterRules;
	}

	public LagartoDomBuilderConfig setUseFosterRules(final boolean useFosterRules) {
		this.useFosterRules = useFosterRules;
		return this;
	}

	public boolean isIgnoreWhitespacesBetweenTags() {
		return ignoreWhitespacesBetweenTags;
	}

	/**
	 * Specifies if whitespaces between open/closed tags should be ignored.
	 */
	public LagartoDomBuilderConfig setIgnoreWhitespacesBetweenTags(final boolean ignoreWhitespacesBetweenTags) {
		this.ignoreWhitespacesBetweenTags = ignoreWhitespacesBetweenTags;
		return this;
	}

	public boolean isIgnoreComments() {
		return ignoreComments;
	}

	/**
	 * Specifies if comments should be ignored in DOM tree.
	 */
	public LagartoDomBuilderConfig setIgnoreComments(final boolean ignoreComments) {
		this.ignoreComments = ignoreComments;
		return this;
	}

	public boolean isEnabledVoidTags() {
		return enabledVoidTags;
	}

	/**
	 * Enables usage of void tags.
	 */
	public LagartoDomBuilderConfig setEnabledVoidTags(final boolean enabledVoidTags) {
		this.enabledVoidTags = enabledVoidTags;
		return this;
	}

	public boolean isSelfCloseVoidTags() {
		return selfCloseVoidTags;
	}

	/**
	 * Specifies if void tags should be self closed.
	 */
	public LagartoDomBuilderConfig setSelfCloseVoidTags(final boolean selfCloseVoidTags) {
		this.selfCloseVoidTags = selfCloseVoidTags;
		return this;
	}

	public boolean isCollectErrors() {
		return collectErrors;
	}

	/**
	 * Enables error collection during parsing.
	 */
	public LagartoDomBuilderConfig setCollectErrors(final boolean collectErrors) {
		this.collectErrors = collectErrors;
		return this;
	}

	public float getCondCommentIEVersion() {
		return condCommentIEVersion;
	}

	public LagartoDomBuilderConfig setCondCommentIEVersion(final float condCommentIEVersion) {
		this.condCommentIEVersion = condCommentIEVersion;
		return this;
	}

	public boolean isImpliedEndTags() {
		return impliedEndTags;
	}

	/**
	 * Enables implied end tags for certain tags.
	 * This flag reduces the performances a bit, so if you
	 * are dealing with 'straight' html that uses closes
	 * tags, consider switching this flag off.
	 */
	public LagartoDomBuilderConfig setImpliedEndTags(final boolean impliedEndTags) {
		this.impliedEndTags = impliedEndTags;
		return this;
	}

	/**
	 * Returns parsing error log level.
	 */
	public Logger.Level getParsingErrorLogLevel() {
		return parsingErrorLogLevel;
	}

	/**
	 * Sets parsing error log level as a name.
	 */
	public LagartoDomBuilderConfig setParsingErrorLogLevelName(String logLevel) {
		logLevel = logLevel.trim().toUpperCase();

		parsingErrorLogLevel = Logger.Level.valueOf(logLevel);

		return this;
	}

	public LagartoHtmlRenderer getLagartoHtmlRenderer() {
		return lagartoHtmlRenderer;
	}

	public void setLagartoHtmlRenderer(final LagartoHtmlRenderer lagartoHtmlRenderer) {
		this.lagartoHtmlRenderer = lagartoHtmlRenderer;
	}
}


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

/**
 * Common stuff for both naming strategies.
 */
abstract class BaseNamingStrategy {

	// ---------------------------------------------------------------- common properties

	protected boolean splitCamelCase = true;
	protected char separatorChar = '_';
	protected boolean changeCase = true;
	protected boolean uppercase = true;
	protected boolean strictAnnotationNames = true;
	protected boolean alwaysQuoteNames = false;
	protected char quoteChar = 0;

	public boolean isSplitCamelCase() {
		return splitCamelCase;
	}

	/**
	 * Specifies if camel case name has to be split.
	 * If set to false, then name is passed unchanged.
	 */
	public void setSplitCamelCase(final boolean splitCamelCase) {
		this.splitCamelCase = splitCamelCase;
	}

	public char getSeparatorChar() {
		return separatorChar;
	}

	/**
	 * Separator character, when camel case names
	 * are {@link #setSplitCamelCase(boolean) split}.
	 */
	public void setSeparatorChar(final char separatorChar) {
		this.separatorChar = separatorChar;
	}

	public boolean isChangeCase() {
		return changeCase;
	}

	/**
	 * Specifies if database names should be convert to
	 * uppercase or lowercase.
	 */
	public void setChangeCase(final boolean changeCase) {
		this.changeCase = changeCase;
	}

	public boolean isUppercase() {
		return uppercase;
	}

	/**
	 * Specifies if table name should be converted to uppercase.
	 * Table names includes prefix and suffix. Otherwise, table name
	 * will be converted to lowercase.
	 */
	public void setUppercase(final boolean uppercase) {
		this.uppercase = uppercase;
	}

	public boolean isLowercase() {
		return !uppercase;
	}

	/**
	 * Alternative property to {@link #setUppercase(boolean)}.
	 * Does just the opposite.
	 */
	public void setLowercase(final boolean lowercase) {
		this.uppercase = !lowercase;
	}

	public boolean isStrictAnnotationNames() {
		return strictAnnotationNames;
	}

	/**
	 * Defines if annotation names are strict, or if all the naming
	 * rules should apply on them, too.
	 */
	public void setStrictAnnotationNames(final boolean strictAnnotationNames) {
		this.strictAnnotationNames = strictAnnotationNames;
	}

	public boolean isAlwaysQuoteNames() {
		return alwaysQuoteNames;
	}

	/**
	 * Defines if all table and column names should be quoted.
	 */
	public void setAlwaysQuoteNames(final boolean alwaysQuoteNames) {
		this.alwaysQuoteNames = alwaysQuoteNames;
	}

	public char getQuoteChar() {
		return quoteChar;
	}

	/**
	 * Defines quote char.
	 */
	public void setQuoteChar(final char quoteChar) {
		this.quoteChar = quoteChar;
	}

	// ---------------------------------------------------------------- util methods

	protected static StringBuilder toUppercase(final StringBuilder string) {
		final int strLen = string.length();

		for (int i = 0; i < strLen; i++) {
			char c = string.charAt(i);

			char uppercaseChar = Character.toUpperCase(c);
			if (c != uppercaseChar) {
				string.setCharAt(i, uppercaseChar);
			}
		}
		return string;
	}

	protected static StringBuilder toLowercase(final StringBuilder string) {
		final int strLen = string.length();

		for (int i = 0; i < strLen; i++) {
			char c = string.charAt(i);

			char lowercaseChar = Character.toLowerCase(c);
			if (c != lowercaseChar) {
				string.setCharAt(i, lowercaseChar);
			}
		}
		return string;
	}

}


// 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.decora.parser;

import jodd.lagarto.Tag;
import jodd.util.CharSequenceUtil;

/**
 * Decora tag, defined by the name and its position in decorator file.
 */
public class DecoraTag {

	private final String name;
	private final String id;
	private final int start;
	private final int end;
	private final int defaultValueStart;
	private final int defaultValueLength;

	public DecoraTag(final String name, final String id, final int start, final int end) {
		this(name, id, start, end, -1, 0);
	}

	public DecoraTag(final String name, final String id, final int start, final int end, int defaultValueStart, final int defaultValueLength) {
		this.name = name;
		this.id = id;
		this.start = start;
		this.end = end;
		if (defaultValueLength == 0) {
			defaultValueStart = -1;
		}
		this.defaultValueStart = defaultValueStart;
		this.defaultValueLength = defaultValueLength;
	}

	/**
	 * Duplicates static content of the class, for caching purposes.
	 * If a decorator is static content, it does not have to be
	 * parsed all over again. Instead, just store parsed list of
	 * DecoraTags and duplicate it.
	 */
	public DecoraTag duplicate() {
		return new DecoraTag(name, id, start, end, defaultValueStart, defaultValueLength);
	}

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

	/**
	 * Returns start index of Decora tag in decorator file.
	 */
	public int getStartIndex() {
		return start;
	}

	/**
	 * Returns end index of Decora tag in decorator file.
	 */
	public int getEndIndex() {
		return end;
	}

	/**
	 * Returns start index of Decora default value if exist.
	 * Returns -1 if default value is not specified.
	 */
	public int getDefaultValueStart() {
		return defaultValueStart;
	}

	/**
	 * Returns the length of Decora default value if exist.
	 * Returns 0 if default value is not specified.
	 */
	public int getDefaultValueLength() {
		return defaultValueLength;
	}

	/**
	 * Returns true if Decora tag has
	 * default value.
	 */
	public boolean hasDefaultValue() {
		return defaultValueStart != -1;
	}

	/**
	 * Returns true if provided tag
	 * matches decorator tag.
	 */
	public boolean isMatchedTag(final Tag tag) {
		if (!tag.nameEquals(name)) {
			return false;
		}
		if (id != null) {
			CharSequence tagId = tag.getId();

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

			if (!CharSequenceUtil.equals(id, tagId)) {
				return false;
			}
		}
		return true;
	}

	// ---------------------------------------------------------------- region

	private int regionStart = -1;
	private int regionLength;

	private int regionTagStart;
	private int regionTagEnd;

	private int deepLevel;

	/**
	 * Returns true if region is fully defined.
	 * Returns false if region is either started or
	 * undefined.
	 */
	public boolean isRegionDefined() {
		return regionLength != 0;
	}

	/**
	 * Returns true if region definition is started.
	 * Region start is known, but not yet the end.
	 */
	public boolean isRegionStarted() {
		return (regionStart != -1) && (regionLength == 0);
	}

	/**
	 * Returns true if region is undefined.
	 * Returns false if region is either started
	 * or defined.
	 */
	public boolean isRegionUndefined() {
		return regionStart == -1;
	}

	/**
	 * Starts defining region by setting the start index and reset region length to zero.
	 */
	public void startRegion(final int start, final int tagLen, final int deepLevel) {
		this.regionStart = start + tagLen;
		this.regionLength = 0;
		this.regionTagStart = start;
		this.deepLevel = deepLevel;
	}

	/**
	 * Ends region definition by setting the region length.
	 */
	public void endRegion(final int regionEnd, final int tagLen) {
		this.regionLength = regionEnd - regionStart;
		this.regionTagEnd = regionEnd + tagLen;
	}

	/**
	 * Returns start index of the region in the page.
	 */
	public int getRegionStart() {
		return regionStart;
	}

	/**
	 * Returns region length.
	 */
	public int getRegionLength() {
		return regionLength;
	}

	/**
	 * Returns true if region of this Decora tag
	 * is inside of region of provided Decora tag.
	 */
	public boolean isInsideOtherTagRegion(final DecoraTag decoraTag) {
		return (regionStart > decoraTag.getRegionStart()) && (regionStart < decoraTag.getRegionStart() + decoraTag.getRegionLength());
	}

	public int getRegionTagStart() {
		return regionTagStart;
	}

	public int getRegionTagEnd() {
		return regionTagEnd;
	}

	public int getDeepLevel() {
		return deepLevel;
	}

	@Override
	public String toString() {
		return "DecoraTag{" +
				"name='" + name + '\'' +
				", start=" + start +
				", end=" + end +
				", regionStart=" + regionStart +
				", regionLength=" + regionLength +
				'}';
	}
}


// 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.lagarto.dom;

import jodd.util.StringUtil;

/**
 * Elements attribute.
 */
public class Attribute implements Cloneable {

	protected final String rawName;
	protected final String name;
	protected String value;
	protected String[] splits;

	public Attribute(final String rawName, final String name, final String value) {
		this.rawName = rawName;
		this.name = name;
		this.value = value;
	}

	@Override
	public Attribute clone() {
		return new Attribute(rawName, name, value);
	}

	/**
	 * Returns attributes raw name.
	 */
	public String getRawName() {
		return rawName;
	}

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

	/**
	 * Returns attribute value.
	 */
	public String getValue() {
		return value;
	}

	/**
	 * Sets attribute value.
	 */
	public void setValue(final String value) {
		this.value = value;
	}

	// ---------------------------------------------------------------- splits

	/**
	 * Returns true if attribute is containing some value.
	 */
	public boolean isContaining(final String include) {
		if (value == null) {
			return false;
		}
		if (splits == null) {
			splits = StringUtil.splitc(value, ' ');
		}

		for (String s: splits) {
			if (s.equals(include)) {
				return true;
			}
		}
		return false;
	}
}


// 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.CSSellyException;
import jodd.csselly.Selector;
import jodd.lagarto.dom.Node;
import jodd.lagarto.dom.NodeFilter;
import jodd.lagarto.dom.NodeListFilter;
import jodd.util.ClassUtil;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Pseudo function selector.
 */
public class PseudoFunctionSelector<E> extends Selector implements NodeFilter, NodeListFilter {

	protected static final Map<String, PseudoFunction> PSEUDO_FUNCTION_MAP;

	static {
		PSEUDO_FUNCTION_MAP = new HashMap<>(8);

		registerPseudoFunction(PseudoFunction.NTH_CHILD.class);
		registerPseudoFunction(PseudoFunction.NTH_LAST_CHILD.class);
		registerPseudoFunction(PseudoFunction.NTH_LAST_OF_TYPE.class);
		registerPseudoFunction(PseudoFunction.NTH_OF_TYPE.class);

		registerPseudoFunction(PseudoFunction.EQ.class);
		registerPseudoFunction(PseudoFunction.GT.class);
		registerPseudoFunction(PseudoFunction.LT.class);
		registerPseudoFunction(PseudoFunction.CONTAINS.class);

		registerPseudoFunction(PseudoFunction.HAS.class);
		registerPseudoFunction(PseudoFunction.NOT.class);
	}

	/**
	 * Registers pseudo function.
	 */
	public static void registerPseudoFunction(final Class<? extends PseudoFunction> pseudoFunctionType) {
		PseudoFunction pseudoFunction;
		try {
			pseudoFunction = ClassUtil.newInstance(pseudoFunctionType);
		} catch (Exception ex) {
			throw new CSSellyException(ex);
		}
		PSEUDO_FUNCTION_MAP.put(pseudoFunction.getPseudoFunctionName(), pseudoFunction);
	}

	/**
	 * Lookups pseudo function for given pseudo function name.
	 */
	public static PseudoFunction<?> lookupPseudoFunction(final String pseudoFunctionName) {
		PseudoFunction pseudoFunction = PSEUDO_FUNCTION_MAP.get(pseudoFunctionName);
		if (pseudoFunction == null) {
			throw new CSSellyException("Unsupported pseudo function: " + pseudoFunctionName);
		}
		return pseudoFunction;
	}

	// ---------------------------------------------------------------- selector

	protected final PseudoFunction<E> pseudoFunction;
	protected final String expression;
	protected final E parsedExpression;

	/**
	 * Creates pseudo function selector for given function and expression.
	 */
	@SuppressWarnings("unchecked")
	public PseudoFunctionSelector(final String functionName, final String expression) {
		super(Type.PSEUDO_FUNCTION);
		this.pseudoFunction = (PseudoFunction<E>) lookupPseudoFunction(functionName.trim());
		this.expression = expression;
		this.parsedExpression = pseudoFunction.parseExpression(expression);
	}

	/**
	 * Returns {@link PseudoFunction pseudo function}.
	 */
	public PseudoFunction<E> getPseudoFunction() {
		return pseudoFunction;
	}

	/**
	 * Returns expression string.
	 */
	public String getExpression() {
		return expression;
	}

	/**
	 * Returns parsed expression object.
	 */
	public E getParsedExpression() {
		return parsedExpression;
	}

	/**
	 * Matches nodes with this pseudo function selector.
	 */
	@Override
	public boolean accept(final Node node) {
		return pseudoFunction.match(node, parsedExpression);
	}

	/**
	 * Accepts node within selected results. Invoked after results are matched.
	 */
	@Override
	public boolean accept(final List<Node> currentResults, final Node node, final int index) {
		return pseudoFunction.match(currentResults, node, index, parsedExpression);
	}

}


// 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.proxetta.asm;

import jodd.asm.AnnotationVisitorAdapter;
import jodd.asm.AsmUtil;
import jodd.asm.EmptyClassVisitor;
import jodd.asm7.AnnotationVisitor;
import jodd.asm7.Attribute;
import jodd.asm7.ClassReader;
import jodd.asm7.ClassVisitor;
import jodd.asm7.FieldVisitor;
import jodd.asm7.MethodVisitor;
import jodd.proxetta.ProxettaException;
import jodd.proxetta.ProxettaNames;
import jodd.proxetta.ProxyAspect;

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

import static jodd.asm7.Opcodes.ACC_ABSTRACT;
import static jodd.asm7.Opcodes.ALOAD;
import static jodd.asm7.Opcodes.INVOKESPECIAL;
import static jodd.asm7.Opcodes.INVOKESTATIC;
import static jodd.asm7.Opcodes.RETURN;
import static jodd.proxetta.asm.ProxettaAsmUtil.CLINIT;
import static jodd.proxetta.asm.ProxettaAsmUtil.DESC_VOID;
import static jodd.proxetta.asm.ProxettaAsmUtil.INIT;

/**
 * Proxetta class builder.
 */
public class ProxettaClassBuilder extends EmptyClassVisitor {

	protected final ProxyAspect[] aspects;
	protected final String suffix;
	protected final String reqProxyClassName;
	protected final TargetClassInfoReader targetClassInfo;

	protected final WorkData wd;

	/**
	 * Constructs new Proxetta class builder.
	 * @param dest			destination visitor
	 * @param aspects		set of aspects to apply
	 * @param suffix		proxy class name suffix, may be null
	 * @param reqProxyClassName		requested proxy class name, may be nulls
	 * @param targetClassInfoReader	target info reader, already invoked.
	 */
	public ProxettaClassBuilder(final ClassVisitor dest, final ProxyAspect[] aspects, final String suffix, final String reqProxyClassName, final TargetClassInfoReader targetClassInfoReader) {
		this.wd = new WorkData(dest);
		this.aspects = aspects;
		this.suffix = suffix;
		this.reqProxyClassName = reqProxyClassName;
		this.targetClassInfo = targetClassInfoReader;
	}

	/**
	 * Returns working data.
	 */
	public WorkData getWorkData() {
		return wd;
	}

	// ---------------------------------------------------------------- header

	/**
	 * Creates destination subclass header from current target class. Destination name is created from targets by
	 * adding a suffix and, optionally, a number. Destination extends the target.
	 */
	@Override
	public void visit(final int version, int access, final String name, final String signature, final String superName, final String[] interfaces) {
		wd.init(name, superName, this.suffix, this.reqProxyClassName);

		// change access of destination
		access &= ~AsmUtil.ACC_ABSTRACT;

		// write destination class
		final int v = ProxettaAsmUtil.resolveJavaVersion(version);
		wd.dest.visit(v, access, wd.thisReference, signature, wd.superName, null);

		wd.proxyAspects = new ProxyAspectData[aspects.length];
		for (int i = 0; i < aspects.length; i++) {
			wd.proxyAspects[i] = new ProxyAspectData(wd, aspects[i], i);
		}
	}

	// ---------------------------------------------------------------- methods and fields

	/**
	 * Creates proxified methods and constructors.
	 * Destination proxy will have all constructors as a target class, using {@link jodd.proxetta.asm.ProxettaCtorBuilder}.
	 * Static initializers are removed, since they will be execute in target anyway.
	 * For each method, {@link ProxettaMethodBuilder} determines if method matches pointcut. If so, method will be proxified.
	 */
	@Override
	public MethodVisitor visitMethod(final int access, final String name, final String desc, final String signature, final String[] exceptions) {
		final MethodSignatureVisitor msign = targetClassInfo.lookupMethodSignatureVisitor(access, name, desc, wd.superReference);
		if (msign == null) {
			return null;
		}
		if (msign.isFinal && !wd.allowFinalMethods) {
			return null;
		}

		// destination constructors [A1]
		if (name.equals(INIT)) {
			MethodVisitor mv = wd.dest.visitMethod(access, name, desc, msign.getAsmMethodSignature(), null);
			return new ProxettaCtorBuilder(mv, msign, wd);
		}
		// ignore destination static block
		if (name.equals(CLINIT)) {
			return null;
		}
		return applyProxy(msign);
	}

	/**
	 * Ignores fields. Fields are not copied to the destination.
	 */
	@Override
	public FieldVisitor visitField(final int access, final String name, final String desc, final String signature, final Object value) {
		return null;
	}

	// ---------------------------------------------------------------- annotation

	/**
	 * Copies all destination type annotations to the target.
	 */
	@Override
	public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) {
		AnnotationVisitor destAnn = wd.dest.visitAnnotation(desc, visible); // [A3]
		return new AnnotationVisitorAdapter(destAnn);
	}

	// ---------------------------------------------------------------- end

	/**
	 * Finalizes creation of destination proxy class.
	 */
	@Override
	public void visitEnd() {
		makeStaticInitBlock();

		makeProxyConstructor();

		processSuperMethods();

		wd.dest.visitEnd();
	}

	/**
	 * Creates static initialization block that simply calls all
	 * advice static init methods in correct order.
	 */
	protected void makeStaticInitBlock() {
		if (wd.adviceClinits != null) {
			MethodVisitor mv = wd.dest.visitMethod(AsmUtil.ACC_STATIC, CLINIT, DESC_VOID, null, null);
			mv.visitCode();
			for (String name : wd.adviceClinits) {
				mv.visitMethodInsn(
					INVOKESTATIC,
					wd.thisReference,
					name, DESC_VOID,
					false);
			}
			mv.visitInsn(RETURN);
			mv.visitMaxs(0, 0);
			mv.visitEnd();
		}
	}

	/**
	 * Creates init method that simply calls all advice constructor methods in correct order.
	 * This created init method is called from each destination's constructor.
	 */
	protected void makeProxyConstructor() {
		MethodVisitor mv = wd.dest.visitMethod(AsmUtil.ACC_PRIVATE | AsmUtil.ACC_FINAL, ProxettaNames.initMethodName, DESC_VOID, null, null);
		mv.visitCode();
		if (wd.adviceInits != null) {
			for (String name : wd.adviceInits) {
				mv.visitVarInsn(ALOAD, 0);
				mv.visitMethodInsn
					(INVOKESPECIAL,
						wd.thisReference,
						name, DESC_VOID,
						false);
			}
		}
		mv.visitInsn(RETURN);
		mv.visitMaxs(0, 0);
		mv.visitEnd();
	}

	/**
	 * Checks for all public super methods that are not overridden.
	 */
	protected void processSuperMethods() {

		for (ClassReader cr : targetClassInfo.superClassReaders) {
			cr.accept(new EmptyClassVisitor() {

				String declaredClassName;

				@Override
				public void visit(final int version, final int access, final String name, final String signature, final String superName, final String[] interfaces) {
					declaredClassName = name;
				}

				@Override
				public MethodVisitor visitMethod(final int access, final String name, final String desc, final String signature, final String[] exceptions) {
					if (name.equals(INIT) || name.equals(CLINIT)) {
						return null;
					}
					MethodSignatureVisitor msign = targetClassInfo.lookupMethodSignatureVisitor(access, name, desc, declaredClassName);
					if (msign == null) {
						return null;
					}
					return applyProxy(msign);
				}
			}, 0);
		}
	}

	// ---------------------------------------------------------------- not used

	/**
     * Visits the source of the class (not used).
     */
    @Override
	public void visitSource(final String source, final String debug) {
		// not used
	}

	/**
     * Visits the enclosing class of the class (not used).
	 */
	@Override
	public void visitOuterClass(final String owner, final String name, final String desc) {
		// not used
	}

    /**
     * Visits a non standard attribute of the class (not used).
     */
	@Override
	public void visitAttribute(final Attribute attr) {
		// not used
	}

	/**
     * Visits information about an inner class (not used).
	 */
	@Override
	public void visitInnerClass(final String name, final String outerName, final String innerName, final int access) {
		// not used
	}

	// ---------------------------------------------------------------- create proxy method builder if needed

	/**
	 * Check if proxy should be applied on method and return proxy method builder if so.
	 * Otherwise, returns null.
	 */
	protected ProxettaMethodBuilder applyProxy(final MethodSignatureVisitor msign) {
		List<ProxyAspectData> aspectList = matchMethodPointcuts(msign);

		if (aspectList == null) {
			// no pointcuts on this method, return
			return null;
		}

		int access = msign.getAccessFlags();
		if ((access & ACC_ABSTRACT) != 0) {
			throw new ProxettaException("Unable to process abstract method: " + msign);
		}

		wd.proxyApplied = true;
		return new ProxettaMethodBuilder(msign, wd, aspectList);
	}

	/**
	 * Matches pointcuts on method. If no pointcut found, returns null.
	 */
	protected List<ProxyAspectData> matchMethodPointcuts(final MethodSignatureVisitor msign) {
		List<ProxyAspectData> aspectList = null;
		for (ProxyAspectData aspectData : wd.proxyAspects) {
			if (aspectData.apply(msign)) {
				if (aspectList == null) {
					aspectList = new ArrayList<>(wd.proxyAspects.length);
				}
				aspectList.add(aspectData);
			}
		}
		return aspectList;
	}

}


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

/**
 * Abstract binary search. It is more abstract then {@link jodd.util.BinarySearch}.
 */
public abstract class BinarySearchBase {

	/**
	 * Compares element at index position with the target.
	 */
	protected abstract int compare(int index);

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

	/**
	 * Finds index of given element in inclusive index range. Returns negative
	 * value if element is not found.
	 */
	public int find(int low, int high) {
		while (low <= high) {
			int mid = (low + high) >>> 1;
			int delta = compare(mid);

			if (delta < 0) {
				low = mid + 1;
			} else if (delta > 0) {
				high = mid - 1;
			} else {
				return mid;
			}
		}
		// not found
		return -(low + 1);
	}

	// ---------------------------------------------------------------- first

	/**
	 * Finds very first index of given element in inclusive index range. Returns negative
	 * value if element is not found.
	 */
	public int findFirst(int low, int high) {

		int ndx = -1;
		while (low <= high) {
			int mid = (low + high) >>> 1;
			int delta = compare(mid);

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

		if (ndx == -1) {
			return -(low + 1);
		}

		return ndx;
	}

	// ---------------------------------------------------------------- last

	/**
	 * Finds very last index of given element in inclusive index range. Returns negative
	 * value if element is not found.
	 */
	public int findLast(int low, int high) {
		int ndx = -1;
		while (low <= high) {
			int mid = (low + high) >>> 1;
			int delta = compare(mid);

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

		if (ndx == -1) {
			return -(low + 1);
		}

		return ndx;
	}

}


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

import jodd.util.StringPool;

/**
 * Chalk allows you to color output going to console.
 * @see Chalk256
 */
public class Chalk<T extends Chalk<T>> {

	/**
	 * Global flag that disables all the chalks.
	 * Useful for windows platforms :)
	 */
	public static boolean enabled = true;

	protected static final String RESET = "0";

	protected static final String BOLD = "1";
	protected static final String UNBOLD = "22";	// 21 isn't widely supported and 22 does the same thing
	protected static final String DIM = "2";
	protected static final String UNDIM = "22";
	protected static final String ITALIC = "3";
	protected static final String UNITALIC = "23";
	protected static final String UNDERLINE = "4";
	protected static final String UNUNDERLINE = "24";
	protected static final String INVERSE = "7";
	protected static final String UNINVERSE = "27";
	protected static final String HIDDEN = "8";
	protected static final String UNHIDDEN = "28";
	protected static final String STRIKETHROUGH = "9";
	protected static final String UNSTRIKETHROUGH = "29";

	protected static final String COLOR_RESET = "39";
	protected static final String BLACK = "30";
	protected static final String RED = "31";
	protected static final String GREEN = "32";
	protected static final String YELLOW = "33";
	protected static final String BLUE = "34";
	protected static final String MAGENTA = "35";
	protected static final String CYAN = "36";
	protected static final String WHITE = "37";
	protected static final String GRAY = "90";

	protected static final String BGCOLOR_RESET = "49";
	protected static final String BGBLACK = "40";
	protected static final String BGRED = "41";
	protected static final String BGGREEN = "42";
	protected static final String BGYELLOW = "43";
	protected static final String BGBLUE = "44";
	protected static final String BGMAGENTA = "45";
	protected static final String BGCYAN = "46";
	protected static final String BGWHITE = "47";

	protected StringBuilder prefix;
	protected StringBuilder suffix;
	protected String text;

	/**
	 * Creates new chalk.
	 */
	public static Chalk chalk() {
		return new Chalk();
	}

	@SuppressWarnings("unchecked")
	protected T _this() {
		return (T) this;
	}

	// ---------------------------------------------------------------- style

	public T bold() {
		startSequence(BOLD);
		endSequence(UNBOLD);
		return _this();
	}

	public T italic() {
		startSequence(ITALIC);
		endSequence(UNITALIC);
		return _this();
	}

	public T dim() {
		startSequence(DIM);
		endSequence(UNDIM);
		return _this();
	}

	public T underline() {
		startSequence(UNDERLINE);
		endSequence(UNUNDERLINE);
		return _this();
	}

	public T inverse() {
		startSequence(INVERSE);
		endSequence(UNINVERSE);
		return _this();
	}

	public T hidden() {
		startSequence(HIDDEN);
		endSequence(UNHIDDEN);
		return _this();
	}
	public T strikeThrough() {
		startSequence(STRIKETHROUGH);
		endSequence(UNSTRIKETHROUGH);
		return _this();
	}

	// ---------------------------------------------------------------- colors

	public T black() {
		startSequence(BLACK);
		endSequence(COLOR_RESET);
		return _this();
	}
	public T red() {
		startSequence(RED);
		endSequence(COLOR_RESET);
		return _this();
	}
	public T green() {
		startSequence(GREEN);
		endSequence(COLOR_RESET);
		return _this();
	}
	public T yellow() {
		startSequence(YELLOW);
		endSequence(COLOR_RESET);
		return _this();
	}
	public T blue() {
		startSequence(BLUE);
		endSequence(COLOR_RESET);
		return _this();
	}
	public T magenta() {
		startSequence(MAGENTA);
		endSequence(COLOR_RESET);
		return _this();
	}
	public T cyan() {
		startSequence(CYAN);
		endSequence(COLOR_RESET);
		return _this();
	}
	public T white() {
		startSequence(WHITE);
		endSequence(COLOR_RESET);
		return _this();
	}
	public T gray() {
		startSequence(GRAY);
		endSequence(COLOR_RESET);
		return _this();
	}
	public T grey() {
		return gray();
	}

	// ---------------------------------------------------------------- bg colors

	public T bgBlack() {
		startSequence(BGBLACK);
		endSequence(BGCOLOR_RESET);
		return _this();
	}
	public T bgRed() {
		startSequence(BGRED);
		endSequence(BGCOLOR_RESET);
		return _this();
	}
	public T bgGreen() {
		startSequence(BGGREEN);
		endSequence(BGCOLOR_RESET);
		return _this();
	}
	public T bgYellow() {
		startSequence(BGYELLOW);
		endSequence(BGCOLOR_RESET);
		return _this();
	}
	public T bgBlue() {
		startSequence(BGBLUE);
		endSequence(BGCOLOR_RESET);
		return _this();
	}
	public T bgMagenta() {
		startSequence(BGMAGENTA);
		endSequence(BGCOLOR_RESET);
		return _this();
	}
	public T bgCyan() {
		startSequence(BGCYAN);
		endSequence(BGCOLOR_RESET);
		return _this();
	}
	public T bgWhite() {
		startSequence(BGWHITE);
		endSequence(BGCOLOR_RESET);
		return _this();
	}

	// ---------------------------------------------------------------- internal

	protected void startSequence(final String value) {
		if (prefix == null) {
			prefix = new StringBuilder();
			prefix.append("\u001B[");
		}
		else {
			prefix.append(StringPool.SEMICOLON);
		}

		prefix.append(value);
	}

	protected void endSequence(final String value) {
		if (suffix == null) {
			suffix = new StringBuilder();
			suffix
				.append("\u001B[")
				.append(value);
		}
		else {
			suffix.insert(2, value + StringPool.SEMICOLON);
		}
	}

	// ---------------------------------------------------------------- out

	/**
	 * Returns chalked string.
	 */
	public String on(final String string) {
		if (!enabled) {
			return string;
		}

		final StringBuilder sb = new StringBuilder();

		if (prefix != null) {
			sb.append(prefix).append("m");
		}

		sb.append(string);

		if (suffix != null) {
			sb.append(suffix).append("m");
		}

		return sb.toString();
	}

	/**
	 * Prints chalked string to system output.
	 */
	public void print(final String string) {
		System.out.print(on(string));
	}

	/**
	 * Prints chalked string to system output.
	 */
	public void println(final String string) {
		System.out.println(on(string));
	}

}