/*
 * LZMAOutputStream
 *
 * Authors: Lasse Collin <lasse.collin@tukaani.org>
 *          Igor Pavlov <http://7-zip.org/>
 *
 * This file has been put into the public domain.
 * You can do whatever you want with this file.
 */

package org.tukaani.xz;

import java.io.OutputStream;
import java.io.IOException;
import org.tukaani.xz.lz.LZEncoder;
import org.tukaani.xz.rangecoder.RangeEncoderToStream;
import org.tukaani.xz.lzma.LZMAEncoder;

/**
 * Compresses into the legacy .lzma file format or into a raw LZMA stream.
 *
 * @since 1.6
 */
public class LZMAOutputStream extends FinishableOutputStream {
    private OutputStream out;

    private final ArrayCache arrayCache;

    private LZEncoder lz;
    private final RangeEncoderToStream rc;
    private LZMAEncoder lzma;

    private final int props;
    private final boolean useEndMarker;
    private final long expectedUncompressedSize;
    private long currentUncompressedSize = 0;

    private boolean finished = false;
    private IOException exception = null;

    private final byte[] tempBuf = new byte[1];

    private LZMAOutputStream(OutputStream out, LZMA2Options options,
                             boolean useHeader, boolean useEndMarker,
                             long expectedUncompressedSize,
                             ArrayCache arrayCache)
            throws IOException {
        if (out == null)
            throw new NullPointerException();

        // -1 indicates unknown and >= 0 are for known sizes.
        if (expectedUncompressedSize < -1)
            throw new IllegalArgumentException(
                    "Invalid expected input size (less than -1)");

        this.useEndMarker = useEndMarker;
        this.expectedUncompressedSize = expectedUncompressedSize;

        this.arrayCache = arrayCache;

        this.out = out;
        rc = new RangeEncoderToStream(out);

        int dictSize = options.getDictSize();
        lzma = LZMAEncoder.getInstance(rc,
                options.getLc(), options.getLp(), options.getPb(),
                options.getMode(),
                dictSize, 0, options.getNiceLen(),
                options.getMatchFinder(), options.getDepthLimit(),
                arrayCache);

        lz = lzma.getLZEncoder();

        byte[] presetDict = options.getPresetDict();
        if (presetDict != null && presetDict.length > 0) {
            if (useHeader)
                throw new UnsupportedOptionsException(
                        "Preset dictionary cannot be used in .lzma files "
                        + "(try a raw LZMA stream instead)");

            lz.setPresetDict(dictSize, presetDict);
        }

        props = (options.getPb() * 5 + options.getLp()) * 9 + options.getLc();

        if (useHeader) {
            // Props byte stores lc, lp, and pb.
            out.write(props);

            // Dictionary size is stored as a 32-bit unsigned little endian
            // integer.
            for (int i = 0; i < 4; ++i) {
                out.write(dictSize & 0xFF);
                dictSize >>>= 8;
            }

            // Uncompressed size is stored as a 64-bit unsigned little endian
            // integer. The max value (-1 in two's complement) indicates
            // unknown size.
            for (int i = 0; i < 8; ++i)
                out.write((int)(expectedUncompressedSize >>> (8 * i)) & 0xFF);
        }
    }

    /**
     * Creates a new compressor for the legacy .lzma file format.
     *

     * If the uncompressed size of the input data is known, it will be stored
     * in the .lzma header and no end of stream marker will be used. Otherwise
     * the header will indicate unknown uncompressed size and the end of stream
     * marker will be used.
     *

     * Note that a preset dictionary cannot be used in .lzma files but
     * it can be used for raw LZMA streams.
     *
     * @param       out         output stream to which the compressed data
     *                          will be written
     *
     * @param       options     LZMA compression options; the same class
     *                          is used here as is for LZMA2
     *
     * @param       inputSize   uncompressed size of the data to be compressed;
     *                          use -1 when unknown
     *
     * @throws      IOException may be thrown from out
     */
    public LZMAOutputStream(OutputStream out, LZMA2Options options,
                            long inputSize)
            throws IOException {
        this(out, options, inputSize, ArrayCache.getDefaultCache());
    }

    /**
     * Creates a new compressor for the legacy .lzma file format.
     *

     * This is identical to
     * LZMAOutputStream(OutputStream, LZMA2Options, long)
     * except that this also takes the arrayCache argument.
     *
     * @param       out         output stream to which the compressed data
     *                          will be written
     *
     * @param       options     LZMA compression options; the same class
     *                          is used here as is for LZMA2
     *
     * @param       inputSize   uncompressed size of the data to be compressed;
     *                          use -1 when unknown
     *
     * @param       arrayCache  cache to be used for allocating large arrays
     *
     * @throws      IOException may be thrown from out
     *
     * @since 1.7
     */
    public LZMAOutputStream(OutputStream out, LZMA2Options options,
                            long inputSize, ArrayCache arrayCache)
            throws IOException {
        this(out, options, true, inputSize == -1, inputSize, arrayCache);
    }

    /**
     * Creates a new compressor for raw LZMA (also known as LZMA1) stream.
     *

     * Raw LZMA streams can be encoded with or without end of stream marker.
     * When decompressing the stream, one must know if the end marker was used
     * and tell it to the decompressor. If the end marker wasn't used, the
     * decompressor will also need to know the uncompressed size.
     *
     * @param       out         output stream to which the compressed data
     *                          will be written
     *
     * @param       options     LZMA compression options; the same class
     *                          is used here as is for LZMA2
     *
     * @param       useEndMarker
     *                          if end of stream marker should be written
     *
     * @throws      IOException may be thrown from out
     */
    public LZMAOutputStream(OutputStream out, LZMA2Options options,
                            boolean useEndMarker) throws IOException {
        this(out, options, useEndMarker, ArrayCache.getDefaultCache());
    }

    /**
     * Creates a new compressor for raw LZMA (also known as LZMA1) stream.
     *

     * This is identical to
     * LZMAOutputStream(OutputStream, LZMA2Options, boolean)
     * except that this also takes the arrayCache argument.
     *
     * @param       out         output stream to which the compressed data
     *                          will be written
     *
     * @param       options     LZMA compression options; the same class
     *                          is used here as is for LZMA2
     *
     * @param       useEndMarker
     *                          if end of stream marker should be written
     *
     * @param       arrayCache  cache to be used for allocating large arrays
     *
     * @throws      IOException may be thrown from out
     *
     * @since 1.7
     */
    public LZMAOutputStream(OutputStream out, LZMA2Options options,
                            boolean useEndMarker, ArrayCache arrayCache)
            throws IOException {
        this(out, options, false, useEndMarker, -1, arrayCache);
    }

    /**
     * Returns the LZMA lc/lp/pb properties encoded into a single byte.
     * This might be useful when handling file formats other than .lzma
     * that use the same encoding for the LZMA properties as .lzma does.
     */
    public int getProps() {
        return props;
    }

    /**
     * Gets the amount of uncompressed data written to the stream.
     * This is useful when creating raw LZMA streams without
     * the end of stream marker.
     */
    public long getUncompressedSize() {
        return currentUncompressedSize;
    }

    public void write(int b) throws IOException {
        tempBuf[0] = (byte)b;
        write(tempBuf, 0, 1);
    }

    public void write(byte[] buf, int off, int len) throws IOException {
        if (off < 0 || len < 0 || off + len < 0 || off + len > buf.length)
            throw new IndexOutOfBoundsException();

        if (exception != null)
            throw exception;

        if (finished)
            throw new XZIOException("Stream finished or closed");

        if (expectedUncompressedSize != -1
                && expectedUncompressedSize - currentUncompressedSize < len)
            throw new XZIOException("Expected uncompressed input size ("
                    + expectedUncompressedSize + " bytes) was exceeded");

        currentUncompressedSize += len;

        try {
            while (len > 0) {
                int used = lz.fillWindow(buf, off, len);
                off += used;
                len -= used;
                lzma.encodeForLZMA1();
            }
        } catch (IOException e) {
            exception = e;
            throw e;
        }
    }

    /**
     * Flushing isn't supported and will throw XZIOException.
     */
    public void flush() throws IOException {
        throw new XZIOException("LZMAOutputStream does not support flushing");
    }

    /**
     * Finishes the stream without closing the underlying OutputStream.
     */
    public void finish() throws IOException {
        if (!finished) {
            if (exception != null)
                throw exception;

            try {
                if (expectedUncompressedSize != -1
                        && expectedUncompressedSize != currentUncompressedSize)
                    throw new XZIOException("Expected uncompressed size ("
                            + expectedUncompressedSize + ") doesn't equal "
                            + "the number of bytes written to the stream ("
                            + currentUncompressedSize + ")");

                lz.setFinishing();
                lzma.encodeForLZMA1();

                if (useEndMarker)
                    lzma.encodeLZMA1EndMarker();

                rc.finish();
            } catch (IOException e) {
                exception = e;
                throw e;
            }

            finished = true;

            lzma.putArraysToCache(arrayCache);
            lzma = null;
            lz = null;
        }
    }

    /**
     * Finishes the stream and closes the underlying OutputStream.
     */
    public void close() throws IOException {
        if (out != null) {
            try {
                finish();
            } catch (IOException e) {}

            try {
                out.close();
            } catch (IOException e) {
                if (exception == null)
                    exception = e;
            }

            out = null;
        }

        if (exception != null)
            throw exception;
    }
}


/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 *
 *
 * This file incorporates work covered by the following copyright and
 * permission notice:
 *
 * Copyright 2004 The Apache Software Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.jasper.runtime;

import java.io.IOException;
import java.io.Writer;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.logging.Logger;
import java.util.logging.Level;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspFactory;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.JspApplicationContext;
import javax.servlet.jsp.tagext.BodyContent;
import javax.servlet.jsp.el.ExpressionEvaluator;
import javax.servlet.jsp.el.VariableResolver;
import javax.servlet.jsp.el.ImplicitObjectELResolver;
import javax.servlet.jsp.el.ScopedAttributeELResolver;

import javax.el.ELException;
import javax.el.ELResolver;
import javax.el.ELContext;
import javax.el.BeanELResolver;
import javax.el.ArrayELResolver;
import javax.el.MapELResolver;
import javax.el.ResourceBundleELResolver;
import javax.el.ListELResolver;
import javax.el.CompositeELResolver;
import javax.el.ValueExpression;
import javax.el.FunctionMapper;
import javax.el.MethodExpression;
import javax.el.ExpressionFactory;
import javax.el.StaticFieldELResolver;

import org.apache.jasper.Constants;
import org.apache.jasper.compiler.Localizer;
import org.apache.jasper.security.SecurityUtil;

/**
 * Implementation of the PageContext class from the JSP spec.
 *
 * @author Anil K. Vijendran
 * @author Larry Cable
 * @author Hans Bergsten
 * @author Pierre Delisle
 * @author Mark Roth
 * @author Jan Luehe
 * @author Kin-man Chung
 */
public class PageContextImpl extends PageContext {

    // Logger
    private static Logger log = Logger.getLogger(PageContextImpl.class.getName());

    // per-servlet state
    private BodyContentImpl[] outs;
    private int depth;
    private Servlet servlet;
    private ServletConfig config;
    private ServletContext context;
    private JspFactory factory;
    private boolean needsSession;
    private String errorPageURL;
    private int	bufferSize;
    private JspApplicationContextImpl jspApplicationContext;
    private ELResolver elResolver;
    private ELContext elContext;

    // page-scope attributes
    private HashMap<String, Object> attributes;
    private boolean isNametableInitialized;

    // per-request state
    private ServletRequest request;
    private ServletResponse response;
    private HttpSession session;

    // initial output stream
    private JspWriter out;
    private JspWriterImpl baseOut;

    /*
     * Constructor.
     */
    PageContextImpl(JspFactory factory) {
        this.factory = factory;
	this.outs = new BodyContentImpl[0];
	this.attributes = new HashMap<String, Object>(16);
	this.depth = -1;
    }

    public void initialize(Servlet servlet,
			   ServletRequest request,
                           ServletResponse response,
			   String errorPageURL,
                           boolean needsSession,
			   int bufferSize,
                           boolean autoFlush) throws IOException {

	_initialize(servlet, request, response, errorPageURL, needsSession,
		    bufferSize, autoFlush);
    }

    private void _initialize(Servlet servlet,
			     ServletRequest request,
			     ServletResponse response,
			     String errorPageURL,
			     boolean needsSession,
			     int bufferSize,
			     boolean autoFlush) throws IOException {

	// initialize state
	this.servlet = servlet;
	this.config = servlet.getServletConfig();
	this.context = config.getServletContext();
	this.needsSession = needsSession;
	this.errorPageURL = errorPageURL;
	this.bufferSize = bufferSize;
	this.request = request;
 	this.response = response;

	// Setup session (if required)
	if (request instanceof HttpServletRequest && needsSession)
	    this.session = ((HttpServletRequest)request).getSession();
	if (needsSession && session == null)
	    throw new IllegalStateException
                ("Page needs a session and none is available");

        // initialize the initial out ...
        depth = -1;
        if (this.baseOut == null) {
            this.baseOut = new JspWriterImpl(response, bufferSize, autoFlush);
        } else {
            this.baseOut.init(response, bufferSize, autoFlush);
        }
        this.out = baseOut;

        this.isNametableInitialized = false;
        setAttribute(Constants.FIRST_REQUEST_SEEN, "true", APPLICATION_SCOPE);

    }

    private void initializePageScopeNameTable() {
        isNametableInitialized = true;
        // register names/values as per spec
        setAttribute(OUT, this.out);
        setAttribute(REQUEST, request);
        setAttribute(RESPONSE, response);

        if (session != null)
            setAttribute(SESSION, session);

        setAttribute(PAGE, servlet);
        setAttribute(CONFIG, config);
        setAttribute(PAGECONTEXT, this);
        setAttribute(APPLICATION, context);
    }

    public void release() {
        out = baseOut;
	try {
            // Do not flush the buffer even if we're not included (i.e.
            // we are the main page. The servlet will flush it and close
            // the stream.
            ((JspWriterImpl)out).flushBuffer();
	} catch (IOException ex) {
	    log.warning("Internal error flushing the buffer in release()");
	}

	servlet = null;
	config = null;
	context = null;
        elContext = null;
        elResolver = null;
        jspApplicationContext = null;
	needsSession = false;
	errorPageURL = null;
	bufferSize = JspWriter.DEFAULT_BUFFER;
	request = null;
	response = null;
        depth = -1;
	baseOut.recycle();
	session = null;

	attributes.clear();
    }

    public Object getAttribute(final String name) {

        if (name == null) {
            throw new NullPointerException(
                    Localizer.getMessage("jsp.error.attribute.null_name"));
        }

        if (SecurityUtil.isPackageProtectionEnabled()){
            return AccessController.doPrivileged(new PrivilegedAction<Object>(){
                public Object run(){
                    return doGetAttribute(name);
                }
            });
        } else {
            return doGetAttribute(name);
        }

    }

    private Object doGetAttribute(String name){
        if (!isNametableInitialized) {
            initializePageScopeNameTable();
        }
        return attributes.get(name);
    }

    public Object getAttribute(final String name, final int scope) {

        if (name == null) {
            throw new NullPointerException(
                    Localizer.getMessage("jsp.error.attribute.null_name"));
        }

        if (SecurityUtil.isPackageProtectionEnabled()){
            return AccessController.doPrivileged(new PrivilegedAction<Object>(){
                public Object run(){
                    return doGetAttribute(name, scope);
                }
            });
        } else {
            return doGetAttribute(name, scope);
        }

    }

    private Object doGetAttribute(String name, int scope){
        switch (scope) {
            case PAGE_SCOPE:
                if (!isNametableInitialized) {
                    initializePageScopeNameTable();
                }
                return attributes.get(name);

            case REQUEST_SCOPE:
                return request.getAttribute(name);

            case SESSION_SCOPE:
                if (session == null) {
                    throw new IllegalStateException(
                        Localizer.getMessage("jsp.error.page.noSession"));
                }
                return session.getAttribute(name);

            case APPLICATION_SCOPE:
                return context.getAttribute(name);

            default:
                throw new IllegalArgumentException("Invalid scope");
        }
    }

    public void setAttribute(final String name, final Object attribute) {

        if (name == null) {
            throw new NullPointerException(
                    Localizer.getMessage("jsp.error.attribute.null_name"));
        }

        if (SecurityUtil.isPackageProtectionEnabled()){
            AccessController.doPrivileged(new PrivilegedAction<Object>(){
                public Object run(){
                    doSetAttribute(name, attribute);
                    return null;
                }
            });
        } else {
            doSetAttribute(name, attribute);
        }
    }

    private void doSetAttribute(String name, Object attribute){
        if (attribute != null) {
            if (!isNametableInitialized) {
                initializePageScopeNameTable();
            }
            attributes.put(name, attribute);
        } else {
            removeAttribute(name, PAGE_SCOPE);
        }
    }

    public void setAttribute(final String name, final Object o, final int scope) {

        if (name == null) {
            throw new NullPointerException(
                    Localizer.getMessage("jsp.error.attribute.null_name"));
        }

        if (SecurityUtil.isPackageProtectionEnabled()){
            AccessController.doPrivileged(new PrivilegedAction<Object>(){
                public Object run(){
                    doSetAttribute(name, o, scope);
                    return null;
                }
            });
        } else {
            doSetAttribute(name, o, scope);
        }

    }

    private void doSetAttribute(String name, Object o, int scope ){
        if (o != null) {
            switch (scope) {
            case PAGE_SCOPE:
                if (!isNametableInitialized) {
                    initializePageScopeNameTable();
                }
                attributes.put(name, o);
                break;

            case REQUEST_SCOPE:
                request.setAttribute(name, o);
                break;

            case SESSION_SCOPE:
                if (session == null) {
                    throw new IllegalStateException(
                        Localizer.getMessage("jsp.error.page.noSession"));
                }
                session.setAttribute(name, o);
                break;

            case APPLICATION_SCOPE:
                context.setAttribute(name, o);
                break;

            default:
                throw new IllegalArgumentException("Invalid scope");
            }
        } else {
            removeAttribute(name, scope);
        }
    }

    public void removeAttribute(final String name, final int scope) {

        if (name == null) {
            throw new NullPointerException(
                    Localizer.getMessage("jsp.error.attribute.null_name"));
        }
        if (SecurityUtil.isPackageProtectionEnabled()){
            AccessController.doPrivileged(new PrivilegedAction<Object>(){
                public Object run(){
                    doRemoveAttribute(name, scope);
                    return null;
                }
            });
        } else {
            doRemoveAttribute(name, scope);
        }
    }

    private void doRemoveAttribute(String name, int scope){
        switch (scope) {
        case PAGE_SCOPE:
            if (!isNametableInitialized) {
                initializePageScopeNameTable();
            }
            attributes.remove(name);
            break;

        case REQUEST_SCOPE:
            request.removeAttribute(name);
            break;

        case SESSION_SCOPE:
            if (session == null) {
            throw new IllegalStateException(
                        Localizer.getMessage("jsp.error.page.noSession"));
            }
            session.removeAttribute(name);
            break;

        case APPLICATION_SCOPE:
            context.removeAttribute(name);
            break;

        default:
            throw new IllegalArgumentException("Invalid scope");
        }
    }

    public int getAttributesScope(final String name) {

        if (name == null) {
            throw new NullPointerException(
                    Localizer.getMessage("jsp.error.attribute.null_name"));
        }

        if (SecurityUtil.isPackageProtectionEnabled()){
            return (AccessController.doPrivileged(new PrivilegedAction<Integer>(){
                public Integer run(){
                    return Integer.valueOf(doGetAttributeScope(name));
                }
            })).intValue();
        } else {
            return doGetAttributeScope(name);
        }
    }

    private int doGetAttributeScope(String name){

        if (!isNametableInitialized) {
            initializePageScopeNameTable();
        }

        if (attributes.get(name) != null)
            return PAGE_SCOPE;

        if (request.getAttribute(name) != null)
            return REQUEST_SCOPE;

        if (session != null) {
            try {
                if (session.getAttribute(name) != null)
                    return SESSION_SCOPE;
            } catch (IllegalStateException ex) {
 	        // Session has been invalidated.
                // Ignore and fall through to application scope.
            }
        }

        if (context.getAttribute(name) != null)
            return APPLICATION_SCOPE;

        return 0;
    }

    public Object findAttribute(final String name) {
        if (SecurityUtil.isPackageProtectionEnabled()){
            return AccessController.doPrivileged(new PrivilegedAction<Object>(){
                public Object run(){
                    if (name == null) {
                        throw new NullPointerException(
                                Localizer.getMessage("jsp.error.attribute.null_name"));
                    }

                    return doFindAttribute(name);
                }
            });
        } else {
            if (name == null) {
                throw new NullPointerException(
                        Localizer.getMessage("jsp.error.attribute.null_name"));
            }

            return doFindAttribute(name);
        }
    }

    private Object doFindAttribute(String name){

        if (!isNametableInitialized) {
            initializePageScopeNameTable();
        }

        Object o = attributes.get(name);
        if (o != null)
            return o;

        o = request.getAttribute(name);
        if (o != null)
            return o;

 	if (session != null) {
	    try {
	        o = session.getAttribute(name);
 	    } catch (IllegalStateException ex) {
 	        // Session has been invalidated.
                // Ignore and fall through to application scope.
 	    }

 	    if (o != null) return o;
 	}

        return context.getAttribute(name);
    }

    public Enumeration<String> getAttributeNamesInScope(final int scope) {
        if (SecurityUtil.isPackageProtectionEnabled()){
            return AccessController.doPrivileged(
                new PrivilegedAction<Enumeration<String>>(){
                public Enumeration<String> run(){
                    return doGetAttributeNamesInScope(scope);
                }
            });
        } else {
            return doGetAttributeNamesInScope(scope);
        }
    }

    private Enumeration<String> doGetAttributeNamesInScope(int scope){
        switch (scope) {
        case PAGE_SCOPE:
            if (!isNametableInitialized) {
                initializePageScopeNameTable();
            }
            return Collections.enumeration(attributes.keySet());

        case REQUEST_SCOPE:
            return request.getAttributeNames();

        case SESSION_SCOPE:
            if (session == null) {
            throw new IllegalStateException(
                        Localizer.getMessage("jsp.error.page.noSession"));
            }
            return session.getAttributeNames();

        case APPLICATION_SCOPE:
            return context.getAttributeNames();

        default:
            throw new IllegalArgumentException("Invalid scope");
        }
    }

    public void removeAttribute(final String name) {

        if (name == null) {
            throw new NullPointerException(
                    Localizer.getMessage("jsp.error.attribute.null_name"));
        }

        if (SecurityUtil.isPackageProtectionEnabled()){
            AccessController.doPrivileged(new PrivilegedAction<Object>(){
                public Object run(){
                    doRemoveAttribute(name);
                    return null;
                }
            });
        } else {
            doRemoveAttribute(name);
        }
    }

    private void doRemoveAttribute(String name){
        removeAttribute(name, PAGE_SCOPE);
        removeAttribute(name, REQUEST_SCOPE);
        if( session != null ) {
            try {
                removeAttribute(name, SESSION_SCOPE);
            } catch (IllegalStateException ex) {
                // Session has been invalidated.
                // Ignore and fall through to application scope.
            }
        }
        removeAttribute(name, APPLICATION_SCOPE);
    }

    public JspWriter getOut() {
	return out;
    }

    public HttpSession getSession() { return session; }
    public Servlet getServlet() { return servlet; }
    public ServletConfig getServletConfig() { return config; }
    public ServletContext getServletContext() {
	return config.getServletContext();
    }
    public ServletRequest getRequest() { return request; }
    public ServletResponse getResponse() { return response; }

    /**
     * Returns the exception associated with this page context, if any.
     *
     * Added wrapping for Throwables to avoid ClassCastException
     * (see Bugzilla 31171 for details).
     *
     * @return The Exception associated with this page context, if any.
     */
    public Exception getException() {

        Throwable t = JspRuntimeLibrary.getThrowable(request);

        // Only wrap if needed
        if((t != null) && (! (t instanceof Exception))) {
            t = new JspException(t);
        }

        return (Exception) t;
    }

    public Object getPage() { return servlet; }

    private final String getAbsolutePathRelativeToContext(String relativeUrlPath) {
        String path = relativeUrlPath;

        if (!path.startsWith("/")) {
	    String uri = (String)
		request.getAttribute("javax.servlet.include.servlet_path");
	    if (uri == null)
		uri = ((HttpServletRequest) request).getServletPath();
            String baseURI = uri.substring(0, uri.lastIndexOf('/'));
            path = baseURI+'/'+path;
        }

        return path;
    }

    public void include(String relativeUrlPath)
	        throws ServletException, IOException {
        JspRuntimeLibrary.include(request, response, relativeUrlPath, out,
				  true);
    }

    /*
     * No need to execute include inside a privileged block, since it calls
     * ApplicationDispatcher include, which in turn is executed inside a
     * privileged block
     */
    public void include(String relativeUrlPath, boolean flush)
	        throws ServletException, IOException {
        JspRuntimeLibrary.include(request, response, relativeUrlPath, out,
				  flush);
    }

    public VariableResolver getVariableResolver() {
        return new VariableResolverImpl(this);
    }

    private ELResolver getELResolver() {

        if (elResolver == null) {
            // Create a CompositeELResolver
            CompositeELResolver celResolver = new CompositeELResolver();

            celResolver.add(new ImplicitObjectELResolver());
            // Add ELResolvers registered in JspApplicationContext
            JspApplicationContextImpl jaContext = getJspApplicationContext();
            Iterator<ELResolver> it = jaContext.getELResolvers();
            while (it.hasNext()) {
                celResolver.add(it.next());
            }
            ELResolver streamELResolver =
                getExpressionFactory(this).getStreamELResolver();
            if (streamELResolver != null) {
                celResolver.add(streamELResolver);
            }
            celResolver.add(new StaticFieldELResolver());
            celResolver.add(new MapELResolver());
            celResolver.add(new ResourceBundleELResolver());
            celResolver.add(new ListELResolver());
            celResolver.add(new ArrayELResolver());
            celResolver.add(new BeanELResolver());
            celResolver.add(new ScopedAttributeELResolver());
            elResolver = celResolver;
        }
        return elResolver;
    }

    public ELContext getELContext() {
        if (elContext == null) {
            elContext = getJspApplicationContext().createELContext(
                            getELResolver());
            elContext.putContext(javax.servlet.jsp.JspContext.class, this);
            ((ELContextImpl)elContext).setVariableMapper(
                new VariableMapperImpl());
        }
        return elContext;
    }

    JspApplicationContextImpl getJspApplicationContext() {
        if (jspApplicationContext == null) {
            jspApplicationContext =
                JspApplicationContextImpl.findJspApplicationContext(context);
        }
        return jspApplicationContext;
    }

    /*
     * No need to execute forward inside a privileged block, since it calls
     * ApplicationDispatcher forward, which in turn is executed inside a
     * privileged block
     */
    public void forward(String relativeUrlPath)
        throws ServletException, IOException{

        // JSP.4.5 If the buffer was flushed, throw IllegalStateException
        try {
            out.clear();
        } catch (IOException ex) {
            IllegalStateException ise =
                new IllegalStateException(Localizer.getMessage(
                            "jsp.error.attempt_to_clear_flushed_buffer"));
            ise.initCause(ex);
            throw ise;
        }

        // Make sure that the response object is not the wrapper for include
        while (response instanceof ServletResponseWrapperInclude) {
            response = ((ServletResponseWrapperInclude)response).getResponse();
        }

        final String path = getAbsolutePathRelativeToContext(relativeUrlPath);
        String includeUri
            = (String) request.getAttribute(Constants.INC_SERVLET_PATH);

        final ServletResponse fresponse = response;
        final ServletRequest frequest = request;

        if (includeUri != null)
            request.removeAttribute(Constants.INC_SERVLET_PATH);
        try {
            context.getRequestDispatcher(path).forward(request, response);
        } finally {
            if (includeUri != null)
                request.setAttribute(Constants.INC_SERVLET_PATH, includeUri);
            request.setAttribute(Constants.FORWARD_SEEN, "true");
        }
    }

    public BodyContent pushBody() {
	return (BodyContent) pushBody(null);
    }

    // Note that there is a potential memory leak with way BodyContentImpl
    // are pooled.  The "outs" array is extended in pushBody, but not shrinked
    // in popBody; and BodyContentImpl.cb can gets arbritarily large.
    // See https://glassfish.dev.java.net/issues/show_bug.cgi?id=8601
    // Setting FactoryImpl.USE_POOL to false eliminates most of the leak,
    // but not all -- kchung 6/29/2009
    public JspWriter pushBody(Writer writer) {
        depth++;
        if (depth >= outs.length) {
            BodyContentImpl[] newOuts = new BodyContentImpl[depth + 1];
            for (int i=0; i<outs.length; i++) {
                newOuts[i] = outs[i];
            }
            newOuts[depth] = new BodyContentImpl(out);
            outs = newOuts;
        }

	outs[depth].setWriter(writer);
        out = outs[depth];

	// Update the value of the "out" attribute in the page scope
	// attribute namespace of this PageContext
	setAttribute(OUT, out);

        return outs[depth];
    }

    public JspWriter popBody() {
        depth--;
        if (depth >= 0) {
            out = outs[depth];
        } else {
            out = baseOut;
        }

	// Update the value of the "out" attribute in the page scope
	// attribute namespace of this PageContext
	setAttribute(OUT, out);

        return out;
    }

    /**
     * Provides programmatic access to the ExpressionEvaluator.
     * The JSP Container must return a valid instance of an
     * ExpressionEvaluator that can parse EL expressions.
     */
    public ExpressionEvaluator getExpressionEvaluator() {
        return new ExpressionEvaluatorImpl(this);
    }

    public void handlePageException(Exception ex)
        throws IOException, ServletException
    {
        // Should never be called since handleException() called with a
        // Throwable in the generated servlet.
        handlePageException((Throwable) ex);
    }

    public void handlePageException(final Throwable t)
        throws IOException, ServletException
    {
        if (t == null)
            throw new NullPointerException("null Throwable");

        if (SecurityUtil.isPackageProtectionEnabled()){
            try{
                AccessController.doPrivileged(new PrivilegedExceptionAction<Object>(){
                    public Object run() throws Exception{
                        doHandlePageException(t);
                        return null;
                    }
                });
            } catch (PrivilegedActionException e){
                Exception ex =  e.getException();
                if (ex instanceof IOException){
                    throw (IOException)ex;
                } else {
                    throw (ServletException)ex;
                }
            }
        } else {
            doHandlePageException(t);
        }

    }

    private void doHandlePageException(Throwable t)
        throws IOException, ServletException {

        if (errorPageURL != null && !errorPageURL.equals("")) {

            /*
             * Set request attributes.
             * Do not set the javax.servlet.error.exception attribute here
             * (instead, set in the generated servlet code for the error page)
             * in order to prevent the ErrorReportValve, which is invoked as
             * part of forwarding the request to the error page, from
             * throwing it if the response has not been committed (the response
             * will have been committed if the error page is a JSP page).
             */
            request.setAttribute("javax.servlet.jsp.jspException", t);
            request.setAttribute("javax.servlet.error.status_code",
                Integer.valueOf(HttpServletResponse.SC_INTERNAL_SERVER_ERROR));
            request.setAttribute("javax.servlet.error.request_uri",
            ((HttpServletRequest) request).getRequestURI());
            request.setAttribute("javax.servlet.error.servlet_name",
                     config.getServletName());
            try {
                forward(errorPageURL);
            } catch (IllegalStateException ise) {
                include(errorPageURL);
            }

            // The error page could be inside an include.

            Object newException = request.getAttribute("javax.servlet.error.exception");

            // t==null means the attribute was not set.
            if( (newException!= null) && (newException==t) ) {
                request.removeAttribute("javax.servlet.error.exception");
                request.setAttribute(Constants.JSP_ERROR_HANDLED, Boolean.TRUE);
            }

            // now clear the error code - to prevent double handling.
            request.removeAttribute("javax.servlet.error.status_code");
            request.removeAttribute("javax.servlet.error.request_uri");
            request.removeAttribute("javax.servlet.jsp.jspException");

        } else {

            // Otherwise throw the exception wrapped inside a ServletException.
            // Set the exception as the root cause in the ServletException
            // to get a stack trace for the real problem
            if (t instanceof IOException) throw (IOException)t;
            if (t instanceof ServletException) throw (ServletException)t;
            if (t instanceof RuntimeException) throw (RuntimeException)t;
            if (t instanceof Error) throw (Error)t;

            Throwable rootCause = null;
            if (t instanceof JspException) {
                rootCause = ((JspException) t).getRootCause();
            } else if (t instanceof ELException) {
                rootCause = t.getCause();
            }

            if (rootCause != null) {
                throw new ServletException(t.getMessage(), rootCause);
            }

            throw new ServletException(t);
        }
    }

    private static ExpressionFactory getExpressionFactory(
            PageContext pageContext) {

        PageContextImpl pc =
            (PageContextImpl) JspContextWrapper.getRootPageContext(pageContext);
        return pc.getJspApplicationContext().getExpressionFactory();
    }

    /**
     * Evaluates an EL expression
     *
     * @param expression The expression to be evaluated
     * @param expectedType The expected resulting type
     * @param pageContext The page context
     * @param functionMap Maps prefix and name to Method
     * @return The result of the evaluation
     */
    public static Object evaluateExpression(final String expression,
					    final Class expectedType,
					    final PageContext pageContext,
					    final ProtectedFunctionMapper functionMap)
	throws ELException
    {
	Object retValue;
        if (SecurityUtil.isPackageProtectionEnabled()){
            try {
                retValue = AccessController.doPrivileged(
                    new PrivilegedExceptionAction<Object>(){

                    public Object run() throws Exception{
                        ELContextImpl elContext =
                            (ELContextImpl) pageContext.getELContext();
                        elContext.setFunctionMapper(functionMap);
                        ExpressionFactory expFactory = getExpressionFactory(pageContext);
                        ValueExpression expr =
                             expFactory.createValueExpression(
                                                   elContext,
                                                   expression,
                                                   expectedType);
                        return expr.getValue(elContext);
                    }
                });
            } catch (PrivilegedActionException ex) {
                Exception realEx = ex.getException();
		if (realEx instanceof ELException) {
		    throw (ELException) realEx;
		} else {
		    throw new ELException(realEx);
		}
            }
        } else {
            ELContextImpl elContext = (ELContextImpl)pageContext.getELContext();
            elContext.setFunctionMapper(functionMap);
            ExpressionFactory expFactory = getExpressionFactory(pageContext);
            ValueExpression expr = expFactory.createValueExpression(
                                           elContext,
                                           expression,
                                           expectedType);
            retValue = expr.getValue(elContext);
        }
	return retValue;
    }

    public static ValueExpression getValueExpression(String expression,
                                                     PageContext pageContext,
                                                     Class expectedType,
                                                     FunctionMapper functionMap)
    {
        // ELResolvers are not used in createValueExpression
        ELContextImpl elctxt = (ELContextImpl)pageContext.getELContext();
        elctxt.setFunctionMapper(functionMap);
        ExpressionFactory expFactory = getExpressionFactory(pageContext);
        return expFactory.createValueExpression(elctxt,
                                                expression,
                                                expectedType);
    }

    public static MethodExpression getMethodExpression(
                                               String expression,
                                               PageContext pageContext,
                                               FunctionMapper functionMap,
                                               Class expectedType,
                                               Class[] paramTypes) {

        ELContextImpl elctxt = (ELContextImpl)pageContext.getELContext();
        elctxt.setFunctionMapper(functionMap);
        ExpressionFactory expFactory = getExpressionFactory(pageContext);
        return expFactory.createMethodExpression(
                                    elctxt,
                                    expression,
                                    expectedType,
                                    paramTypes);
    }

    public static void setValueVariable(PageContext pageContext,
                                        String variable,
                                        ValueExpression expression) {
        ELContextImpl elctxt = (ELContextImpl)pageContext.getELContext();
        elctxt.getVariableMapper().setVariable(variable, expression);
    }

    public static void setMethodVariable(PageContext pageContext,
                                         String variable,
                                         MethodExpression expression) {
        ExpressionFactory expFactory = getExpressionFactory(pageContext);
        ValueExpression exp =
            expFactory.createValueExpression(expression, Object.class);
        setValueVariable(pageContext, variable, exp);
    }
}


/*******************************************************************************
 * Copyright (c) 2015, 2016 IBH SYSTEMS GmbH.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBH SYSTEMS GmbH - initial API and implementation
 *******************************************************************************/
package org.eclipse.packagedrone.repo.manage.system.internal;

import java.io.FileReader;
import java.io.Reader;
import java.net.InetAddress;
import java.net.UnknownHostException;

import org.eclipse.packagedrone.repo.manage.system.SystemService;

import com.google.common.io.CharStreams;

public class SystemServiceImpl implements SystemService
{
    private final String hostname;

    public SystemServiceImpl ()
    {
        this.hostname = discoverHostname ();
    }

    public String getHostname ()
    {
        return this.hostname;
    }

    @Override
    public String getDefaultSitePrefix ()
    {
        String prefix;

        prefix = System.getProperty ( "package.drone.site.prefix" );
        if ( prefix != null && !prefix.isEmpty () )
        {
            return prefix;
        }

        prefix = System.getenv ( "PACKAGE_DRONE_SITE_PREFIX" );
        if ( prefix != null && !prefix.isEmpty () )
        {
            return prefix;
        }

        prefix = makePrefixFromOsgiProperties ();
        if ( prefix != null )
        {
            return prefix;
        }

        return null;
    }

    /**
     * Make the prefix by guessing the port from the OSGi settings
     */
    protected String makePrefixFromOsgiProperties ()
    {
        final String port = System.getProperty ( "org.osgi.service.http.port" );
        if ( port == null )
        {
            return null;
        }

        final StringBuilder sb = new StringBuilder ();
        sb.append ( "http://" ).append ( discoverHostname () );
        if ( !"80".equals ( port ) )
        {
            sb.append ( ':' ).append ( port );
        }
        return sb.toString ();
    }

    private static String discoverHostname ()
    {
        String hostname = System.getenv ( "HOSTNAME" );

        if ( hostname == null )
        {
            hostname = System.getenv ( "COMPUTERNAME" );
        }

        if ( hostname == null )
        {
            try ( Reader reader = new FileReader ( "/etc/hostname" ) )
            {
                hostname = CharStreams.toString ( reader ).trim ();
            }
            catch ( final Exception e )
            {
            }
        }

        if ( hostname == null )
        {
            try
            {
                hostname = InetAddress.getLocalHost ().getHostName ();
            }
            catch ( final UnknownHostException e )
            {
            }
        }

        if ( hostname == null )
        {
            // last chance
            hostname = "localhost";
        }

        return hostname;
    }
}


/*******************************************************************************
 * Copyright (c) 2015, 2016 IBH SYSTEMS GmbH.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBH SYSTEMS GmbH - initial API and implementation
 *******************************************************************************/
package org.eclipse.packagedrone.repo.xml;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;

import org.eclipse.packagedrone.repo.xml.internal.Activator;
import org.eclipse.packagedrone.utils.xml.XmlToolsFactory;

/**
 * Helper class for file types
 */
public final class XmlFiles
{
    private XmlFiles ()
    {
    }

    /**
     * Test if this file is an XML file
     *
     * @param path
     *            the path to check
     * @return true if this is an XML file
     * @throws IOException
     *             if the probing fails
     */
    public static boolean isXml ( final Path path ) throws IOException
    {
        final XmlToolsFactory xml = Activator.getXmlToolsFactory ();
        final XMLInputFactory xin = xml.newXMLInputFactory ();

        try ( InputStream stream = new BufferedInputStream ( Files.newInputStream ( path ) ) )
        {
            try
            {
                final XMLStreamReader reader = xin.createXMLStreamReader ( stream );
                reader.next ();
                return true;
            }
            catch ( final XMLStreamException e )
            {
                return false;
            }
        }
    }
}


/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 *
 *
 * This file incorporates work covered by the following copyright and
 * permission notice:
 *
 * Copyright 2004 The Apache Software Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.jasper.compiler;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;

import javax.servlet.jsp.tagext.TagAttributeInfo;
import javax.servlet.jsp.tagext.TagExtraInfo;
import javax.servlet.jsp.tagext.TagFileInfo;
import javax.servlet.jsp.tagext.TagInfo;
import javax.servlet.jsp.tagext.TagLibraryInfo;
import javax.servlet.jsp.tagext.TagVariableInfo;
import javax.servlet.jsp.tagext.VariableInfo;

import org.apache.jasper.Constants;
import org.apache.jasper.JasperException;
import org.apache.jasper.JspCompilationContext;
import org.apache.jasper.servlet.JspServletWrapper;
import org.apache.jasper.runtime.JspSourceDependent;

/**
 * 1. Processes and extracts the directive info in a tag file.
 * 2. Compiles and loads tag files used in a JSP file.
 *
 * @author Kin-man Chung
 */

class TagFileProcessor {

    private ArrayList<Compiler> tempVector;

    /**
     * A visitor the tag file
     */
    private static class TagFileDirectiveVisitor extends Node.Visitor {

        private static final JspUtil.ValidAttribute[] tagDirectiveAttrs = {
            new JspUtil.ValidAttribute("display-name"),
            new JspUtil.ValidAttribute("body-content"),
            new JspUtil.ValidAttribute("dynamic-attributes"),
            new JspUtil.ValidAttribute("small-icon"),
            new JspUtil.ValidAttribute("large-icon"),
            new JspUtil.ValidAttribute("description"),
            new JspUtil.ValidAttribute("example"),
            new JspUtil.ValidAttribute("pageEncoding"),
            new JspUtil.ValidAttribute("language"),
            new JspUtil.ValidAttribute("import"),
            new JspUtil.ValidAttribute("isELIgnored"),
            new JspUtil.ValidAttribute("deferredSyntaxAllowedAsLiteral"),
            new JspUtil.ValidAttribute("trimDirectiveWhitespaces")
        };

        private static final JspUtil.ValidAttribute[] attributeDirectiveAttrs = {
            new JspUtil.ValidAttribute("name", true),
            new JspUtil.ValidAttribute("required"),
            new JspUtil.ValidAttribute("fragment"),
            new JspUtil.ValidAttribute("rtexprvalue"),
            new JspUtil.ValidAttribute("type"),
            new JspUtil.ValidAttribute("description"),
            new JspUtil.ValidAttribute("deferredValue"),
            new JspUtil.ValidAttribute("deferredValueType"),
            new JspUtil.ValidAttribute("deferredMethod"),
            new JspUtil.ValidAttribute("deferredMethodSignature")
        };

        private static final JspUtil.ValidAttribute[] variableDirectiveAttrs = {
            new JspUtil.ValidAttribute("name-given"),
            new JspUtil.ValidAttribute("name-from-attribute"),
            new JspUtil.ValidAttribute("alias"),
            new JspUtil.ValidAttribute("variable-class"),
            new JspUtil.ValidAttribute("scope"),
            new JspUtil.ValidAttribute("declare"),
            new JspUtil.ValidAttribute("description")
        };

        private ErrorDispatcher err;
        private TagLibraryInfo tagLibInfo;

        private String name = null;
        private String path = null;
        private TagExtraInfo tei = null;
        private String bodycontent = null;
        private String description = null;
        private String displayName = null;
        private String smallIcon = null;
        private String largeIcon = null;
        private String dynamicAttrsMapName;
        private String example = null;

        private List<TagAttributeInfo> attributeVector;
        private List<TagVariableInfo> variableVector;

        private HashMap<String, NameEntry> nameTable =
                    new HashMap<String, NameEntry>();
        private HashMap<String, NameEntry> nameFromTable =
                    new HashMap<String, NameEntry>();

        // The tag file's JSP version
        private Double jspVersionDouble;

        private static enum Name {
            ATTR_NAME("name", "attribute"),
            VAR_NAME_GIVEN("name-given", "variable"),
            VAR_NAME_FROM("name-from-attribute", "variable"),
            VAR_ALIAS("alias", "variable"),
            TAG_DYNAMIC("dynamic-attributes", "tag");

            private String attribute;
            private String directive;

            String getAttribute() {
                return this.attribute;
            }

            String getDirective() {
                return this.directive;
            }

            Name(String attribute, String directive) {
                this.attribute = attribute;
                this.directive = directive;
            }
        }

        public TagFileDirectiveVisitor(Compiler compiler,
                                       TagLibraryInfo tagLibInfo,
                                       String name,
                                       String path) {
            err = compiler.getErrorDispatcher();
            this.tagLibInfo = tagLibInfo;
            this.name = name;
            this.path = path;
            attributeVector = new ArrayList<TagAttributeInfo>();
            variableVector = new ArrayList<TagVariableInfo>();

            jspVersionDouble = Double.valueOf(tagLibInfo.getRequiredVersion());
        }

        public void visit(Node.JspRoot n) throws JasperException {
            /*
             * If a tag file in XML syntax contains a jsp:root element, the
             * value of its "version" attribute must match the tag file's JSP
             * version.
             */
            String jspRootVersion = n.getTextAttribute("version");
            if (jspRootVersion == null) {
                err.jspError(n, "jsp.error.mandatory.attribute", n.getQName(),
                             "version");
            }
            if (!jspRootVersion.equals(jspVersionDouble.toString())) {
                err.jspError(n, "jsp.error.tagfile.jspVersionMismatch",
                             jspRootVersion, jspVersionDouble.toString());
            }
            visitBody(n);
        }

        public void visit(Node.TagDirective n) throws JasperException {

            JspUtil.checkAttributes("Tag directive", n, tagDirectiveAttrs,
                                    err);

            bodycontent = checkConflict(n, bodycontent, "body-content");
            if (bodycontent != null &&
                    !bodycontent.equals(TagInfo.BODY_CONTENT_EMPTY) &&
                    !bodycontent.equals(TagInfo.BODY_CONTENT_TAG_DEPENDENT) &&
                    !bodycontent.equals(TagInfo.BODY_CONTENT_SCRIPTLESS)) {
                err.jspError(n, "jsp.error.tagdirective.badbodycontent",
                             bodycontent);
            }
            dynamicAttrsMapName = checkConflict(n, dynamicAttrsMapName,
                                                "dynamic-attributes");
            if (dynamicAttrsMapName != null) {
                checkUniqueName(dynamicAttrsMapName, Name.TAG_DYNAMIC, n);
            }
            smallIcon = checkConflict(n, smallIcon, "small-icon");
            largeIcon = checkConflict(n, largeIcon, "large-icon");
            description = checkConflict(n, description, "description");
            displayName = checkConflict(n, displayName, "display-name");
            example = checkConflict(n, example, "example");

            if (n.getAttributeValue("deferredSyntaxAllowedAsLiteral") != null
                    && Double.compare(jspVersionDouble,
                                      Constants.JSP_VERSION_2_1) < 0) {
                err.jspError("jsp.error.invalidTagDirectiveAttrUnless21",
                             "deferredSyntaxAllowedAsLiteral");
            }

            // Additional tag directives are validated in Validator
        }

        private String checkConflict(Node n, String oldAttrValue, String attr)
                throws JasperException {

            String result = oldAttrValue;
            String attrValue = n.getAttributeValue(attr);
            if (attrValue != null) {
                if (oldAttrValue != null && !oldAttrValue.equals(attrValue)) {
                    err.jspError(n, "jsp.error.tag.conflict.attr", attr,
                                 oldAttrValue, attrValue);
                }
                result = attrValue;
            }
            return result;
        }

        public void visit(Node.AttributeDirective n) throws JasperException {

            JspUtil.checkAttributes("Attribute directive", n,
                                    attributeDirectiveAttrs, err);

            String attrName = n.getAttributeValue("name");
            boolean required = JspUtil.booleanValue(
                                        n.getAttributeValue("required"));
            boolean rtexprvalue = true;
            String rtexprvalueString = n.getAttributeValue("rtexprvalue");
            if (rtexprvalueString != null) {
                rtexprvalue = JspUtil.booleanValue( rtexprvalueString );
            }
            boolean fragment = JspUtil.booleanValue(
                                        n.getAttributeValue("fragment"));
            String type = n.getAttributeValue("type");

            String deferredValue = n.getAttributeValue("deferredValue");
            String deferredMethod = n.getAttributeValue("deferredMethod");
            String expectedType = n.getAttributeValue("deferredValueType");
            String methodSignature = n.getAttributeValue("deferredMethodSignature");
            if (Double.compare(jspVersionDouble,
                               Constants.JSP_VERSION_2_1) < 0) {
                if (deferredValue != null) {
                    err.jspError("jsp.error.invalidAttrDirectiveAttrUnless21",
                                 "deferredValue");
                }
                if (deferredMethod != null) {
                    err.jspError("jsp.error.invalidAttrDirectiveAttrUnless21",
                                 "deferredMethod");
                }
                if (expectedType != null) {
                    err.jspError("jsp.error.invalidAttrDirectiveAttrUnless21",
                                 "deferredValueType");
                }
                if (methodSignature != null) {
                    err.jspError("jsp.error.invalidAttrDirectiveAttrUnless21",
                                 "deferredMethodSignature");
                }
            }

            boolean isDeferredValue = JspUtil.booleanValue(deferredValue);
            boolean isDeferredMethod = JspUtil.booleanValue(deferredMethod);
            if (expectedType == null) {
                if (isDeferredValue) {
                    expectedType = "java.lang.Object";
                }
            }
            else {
                if (deferredValue != null && !isDeferredValue) {
                    err.jspError("jsp.error.deferredvaluewithtype");
                }
                isDeferredValue = true;
            }

            if (methodSignature == null) {
                if (isDeferredMethod) {
                    methodSignature = "void method()";
                }
            }
            else {
                if (deferredMethod != null && !isDeferredMethod) {
                    err.jspError("jsp.error.deferredmethodwithsignature");
                }
                isDeferredMethod = true;
            }

            if (fragment) {
                // type is fixed to "JspFragment" and a translation error
                // must occur if specified.
                if (type != null) {
                    err.jspError(n, "jsp.error.fragmentwithtype");
                }
                // rtexprvalue is fixed to "true" and a translation error
                // must occur if specified.
                rtexprvalue = true;
                if( rtexprvalueString != null ) {
                    err.jspError(n, "jsp.error.frgmentwithrtexprvalue" );
                }
            } else if (type == null) {
                if (isDeferredValue) {
                    type = "javax.el.ValueExpression";
                } else if (isDeferredMethod) {
                    type = "javax.el.MethodExpression";
                } else {
                    type = "java.lang.String";
                }
            } else if (isDeferredValue || isDeferredMethod) {
                err.jspError("jsp.error.deferredwithtype");
            }

            if (isDeferredValue || isDeferredMethod) {
                rtexprvalue = false;
            }
            TagAttributeInfo tagAttributeInfo =
                    new TagAttributeInfo(attrName,
                                         required,
                                         type,
                                         rtexprvalue,
                                         fragment,
                                         description,
                                         isDeferredValue,
                                         isDeferredMethod,
                                         expectedType,
                                         methodSignature);
            attributeVector.add(tagAttributeInfo);
            checkUniqueName(attrName, Name.ATTR_NAME, n, tagAttributeInfo);
        }

        public void visit(Node.VariableDirective n) throws JasperException {

            JspUtil.checkAttributes("Variable directive", n,
                                    variableDirectiveAttrs, err);

            String nameGiven = n.getAttributeValue("name-given");
            String nameFromAttribute = n.getAttributeValue("name-from-attribute");
            if (nameGiven == null && nameFromAttribute == null) {
                err.jspError("jsp.error.variable.either.name");
            }

            if (nameGiven != null && nameFromAttribute != null) {
                err.jspError("jsp.error.variable.both.name");
            }

            String alias = n.getAttributeValue("alias");
            if (nameFromAttribute != null && alias == null ||
                nameFromAttribute == null && alias != null) {
                err.jspError("jsp.error.variable.alias");
            }

            String className = n.getAttributeValue("variable-class");
            if (className == null)
                className = "java.lang.String";

            String declareStr = n.getAttributeValue("declare");
            boolean declare = true;
            if (declareStr != null)
                declare = JspUtil.booleanValue(declareStr);

            int scope = VariableInfo.NESTED;
            String scopeStr = n.getAttributeValue("scope");
            if (scopeStr != null) {
                if ("NESTED".equals(scopeStr)) {
                    // Already the default
                } else if ("AT_BEGIN".equals(scopeStr)) {
                    scope = VariableInfo.AT_BEGIN;
                } else if ("AT_END".equals(scopeStr)) {
                    scope = VariableInfo.AT_END;
                }
            }

            if (nameFromAttribute != null) {
                /*
		 * An alias has been specified. We use 'nameGiven' to hold the
		 * value of the alias, and 'nameFromAttribute' to hold the
		 * name of the attribute whose value (at invocation-time)
		 * denotes the name of the variable that is being aliased
		 */
                nameGiven = alias;
                checkUniqueName(nameFromAttribute, Name.VAR_NAME_FROM, n);
                checkUniqueName(alias, Name.VAR_ALIAS, n);
            }
            else {
                // name-given specified
                checkUniqueName(nameGiven, Name.VAR_NAME_GIVEN, n);
            }

            variableVector.add(new TagVariableInfo(
                                                nameGiven,
                                                nameFromAttribute,
                                                className,
                                                declare,
                                                scope));
        }

        public TagInfo getTagInfo() throws JasperException {

            if (name == null) {
                // XXX Get it from tag file name
            }

            if (bodycontent == null) {
                bodycontent = TagInfo.BODY_CONTENT_SCRIPTLESS;
            }

            String tagClassName = JspUtil.getTagHandlerClassName(path, err);

            TagVariableInfo[] tagVariableInfos
                = variableVector.toArray(new TagVariableInfo[0]);

            TagAttributeInfo[] tagAttributeInfo
                = attributeVector.toArray(new TagAttributeInfo[0]);

            return new JasperTagInfo(name,
			       tagClassName,
			       bodycontent,
			       description,
			       tagLibInfo,
			       tei,
			       tagAttributeInfo,
			       displayName,
			       smallIcon,
			       largeIcon,
			       tagVariableInfos,
			       dynamicAttrsMapName);
        }

        static class NameEntry {
            private Name type;
            private Node node;
            private TagAttributeInfo attr;

            NameEntry(Name type, Node node, TagAttributeInfo attr) {
                this.type = type;
                this.node = node;
                this.attr = attr;
            }

            Name getType() { return type;}
            Node getNode() { return node; }
            TagAttributeInfo getTagAttributeInfo() { return attr; }
        }

        /**
         * Reports a translation error if names specified in attributes of
         * directives are not unique in this translation unit.
         *
         * The value of the following attributes must be unique.
         *   1. 'name' attribute of an attribute directive
         *   2. 'name-given' attribute of a variable directive
         *   3. 'alias' attribute of variable directive
         *   4. 'dynamic-attributes' of a tag directive
         * except that 'dynamic-attributes' can (and must) have the same
         * value when it appears in multiple tag directives.
         *
         * Also, 'name-from' attribute of a variable directive cannot have
         * the same value as that from another variable directive.
         */
        private void checkUniqueName(String name, Name type, Node n)
                throws JasperException {
            checkUniqueName(name, type, n, null);
        }

        private void checkUniqueName(String name, Name type, Node n,
                                     TagAttributeInfo attr)
                throws JasperException {

            HashMap<String, NameEntry> table =
                (type == Name.VAR_NAME_FROM)? nameFromTable: nameTable;
            NameEntry nameEntry = table.get(name);
            if (nameEntry != null) {
                if (type != Name.TAG_DYNAMIC
                        || nameEntry.getType() != Name.TAG_DYNAMIC) {
                    int line = nameEntry.getNode().getStart().getLineNumber();
                    err.jspError(n, "jsp.error.tagfile.nameNotUnique",
                        type.getAttribute(), type.getDirective(),
                        nameEntry.getType().getAttribute(),
                        nameEntry.getType().getDirective(),
                        Integer.toString(line));
                }
            } else {
                table.put(name, new NameEntry(type, n, attr));
            }
        }

        /**
         * Perform miscelleaneous checks after the nodes are visited.
         */
        void postCheck() throws JasperException {
            // Check that var.name-from-attributes has valid values.
	    Iterator iter = nameFromTable.keySet().iterator();
            while (iter.hasNext()) {
                String nameFrom = (String) iter.next();
                NameEntry nameEntry = nameTable.get(nameFrom);
                NameEntry nameFromEntry = nameFromTable.get(nameFrom);
                Node nameFromNode = nameFromEntry.getNode();
                if (nameEntry == null) {
                    err.jspError(nameFromNode,
                                 "jsp.error.tagfile.nameFrom.noAttribute",
                                 nameFrom);
                } else {
                    Node node = nameEntry.getNode();
                    TagAttributeInfo tagAttr = nameEntry.getTagAttributeInfo();
                    if (! "java.lang.String".equals(tagAttr.getTypeName())
                            || ! tagAttr.isRequired()
                            || tagAttr.canBeRequestTime()){
                        err.jspError(nameFromNode,
                            "jsp.error.tagfile.nameFrom.badAttribute",
                            nameFrom,
                            Integer.toString(node.getStart().getLineNumber()));
                     }
                }
            }
        }
    }

    /**
     * Parses the tag file, and collects information on the directives included
     * in it.  The method is used to obtain the info on the tag file, when the
     * handler that it represents is referenced.  The tag file is not compiled
     * here.
     *
     * @param pc the current ParserController used in this compilation
     * @param name the tag name as specified in the TLD
     * @param tagfile the path for the tagfile
     * @param tagLibInfo the TagLibraryInfo object associated with this TagInfo
     * @return a TagInfo object assembled from the directives in the tag file.
     */
    public static TagInfo parseTagFileDirectives(ParserController pc,
						 String name,
						 String path,
						 TagLibraryInfo tagLibInfo)
                        throws JasperException {

        ErrorDispatcher err = pc.getCompiler().getErrorDispatcher();

        Node.Nodes page = null;
        try {
            page = pc.parseTagFileDirectives(path);
        } catch (FileNotFoundException e) {
            err.jspError("jsp.error.file.not.found", path);
        } catch (IOException e) {
            err.jspError("jsp.error.file.not.found", path);
        }

        TagFileDirectiveVisitor tagFileVisitor
            = new TagFileDirectiveVisitor(pc.getCompiler(), tagLibInfo, name,
                                          path);
        page.visit(tagFileVisitor);
        tagFileVisitor.postCheck();

        return tagFileVisitor.getTagInfo();
    }

    /**
     * Compiles and loads a tagfile.
     */
    private Class loadTagFile(Compiler compiler,
                              String tagFilePath, TagInfo tagInfo,
                              PageInfo parentPageInfo)
        throws JasperException {

        JspCompilationContext ctxt = compiler.getCompilationContext();
        JspRuntimeContext rctxt = ctxt.getRuntimeContext();

        synchronized(rctxt) {
            JspServletWrapper wrapper =
                (JspServletWrapper) rctxt.getWrapper(tagFilePath);
            if (wrapper == null) {
                wrapper = new JspServletWrapper(ctxt.getServletContext(),
                                                ctxt.getOptions(),
                                                tagFilePath,
                                                tagInfo,
                                                ctxt.getRuntimeContext(),
                                                (URL) ctxt.getTagFileJarUrls().get(tagFilePath));
                    rctxt.addWrapper(tagFilePath,wrapper);

		// Use same classloader and classpath for compiling tag files
		wrapper.getJspEngineContext().setClassLoader(
				(URLClassLoader) ctxt.getClassLoader());
		wrapper.getJspEngineContext().setClassPath(ctxt.getClassPath());
            }
            else {
                // Make sure that JspCompilationContext gets the latest TagInfo
                // for the tag file.  TagInfo instance was created the last
                // time the tag file was scanned for directives, and the tag
                // file may have been modified since then.
                wrapper.getJspEngineContext().setTagInfo(tagInfo);
            }

            Class tagClazz;
            int tripCount = wrapper.incTripCount();
            try {
                if (tripCount > 0) {
                    // When tripCount is greater than zero, a circular
                    // dependency exists.  The circularily dependant tag
                    // file is compiled in prototype mode, to avoid infinite
                    // recursion.

                    JspServletWrapper tempWrapper
                        = new JspServletWrapper(ctxt.getServletContext(),
                                                ctxt.getOptions(),
                                                tagFilePath,
                                                tagInfo,
                                                ctxt.getRuntimeContext(),
                                                (URL) ctxt.getTagFileJarUrls().get(tagFilePath));
                    tagClazz = tempWrapper.loadTagFilePrototype();
                    tempVector.add(
                               tempWrapper.getJspEngineContext().getCompiler());
                } else {
                    tagClazz = wrapper.loadTagFile();
                }
            } finally {
                wrapper.decTripCount();
            }

            // Add the dependants for this tag file to its parent's
            // dependant list.  The only reliable dependency information
            // can only be obtained from the tag instance.
            try {
                Object tagIns = tagClazz.newInstance();
                if (tagIns instanceof JspSourceDependent) {
                    for (String dependant:
                            ((JspSourceDependent)tagIns).getDependants()) {
                        parentPageInfo.addDependant(dependant);
                    }
                }
            } catch (Exception e) {
                // ignore errors
            }

            return tagClazz;
        }
    }

    /*
     * Visitor which scans the page and looks for tag handlers that are tag
     * files, compiling (if necessary) and loading them.
     */
    private class TagFileLoaderVisitor extends Node.Visitor {

        private Compiler compiler;
        private PageInfo pageInfo;

        TagFileLoaderVisitor(Compiler compiler) {

            this.compiler = compiler;
            this.pageInfo = compiler.getPageInfo();
        }

        public void visit(Node.CustomTag n) throws JasperException {
            TagFileInfo tagFileInfo = n.getTagFileInfo();
            if (tagFileInfo != null) {
                String tagFilePath = tagFileInfo.getPath();
                if (tagFilePath.startsWith("/META-INF/")) {
                    // For tags in JARs, add the TLD and the tag as a dependency
                    String[] location =
                        compiler.getCompilationContext().getTldLocation(
                            tagFileInfo.getTagInfo().getTagLibrary().getURI());
                    // Add TLD
                    pageInfo.addDependant("jar:" + location[0] + "!/" +
                            location[1]);
                    // Add Tag
                    pageInfo.addDependant("jar:" + location[0] + "!" +
                            tagFilePath);
                } else {
                    pageInfo.addDependant(tagFilePath);
		}
                Class c = loadTagFile(compiler, tagFilePath, n.getTagInfo(),
                                      pageInfo);
                n.setTagHandlerClass(c);
            }
            visitBody(n);
        }
    }

    /**
     * Implements a phase of the translation that compiles (if necessary)
     * the tag files used in a JSP files.  The directives in the tag files
     * are assumed to have been proccessed and encapsulated as TagFileInfo
     * in the CustomTag nodes.
     */
    public void loadTagFiles(Compiler compiler, Node.Nodes page)
                throws JasperException {

        tempVector = new ArrayList<Compiler>();
        page.visit(new TagFileLoaderVisitor(compiler));
    }

    /**
     * Removed the java and class files for the tag prototype
     * generated from the current compilation.
     * @param classFileName If non-null, remove only the class file with
     *        with this name.
     */
    public void removeProtoTypeFiles(String classFileName) {
        Iterator<Compiler> iter = tempVector.iterator();
        while (iter.hasNext()) {
            Compiler c = iter.next();
            if (classFileName == null) {
                c.removeGeneratedClassFiles();
            } else if (classFileName.equals(
                        c.getCompilationContext().getClassFileName())) {
                c.removeGeneratedClassFiles();
                tempVector.remove(c);
                return;
            }
        }
    }
}