/*
 * Copyright 2015 Red Hat, Inc. and/or its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 *
 *      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.eclipse.webdav.dom;

import java.util.Enumeration;
import java.util.NoSuchElementException;
import org.eclipse.webdav.Policy;
import org.w3c.dom.*;

/**
 * An element editor for the WebDAV response element. See RFC2518
 * section 12.9.1 for the element's definition.
 *

 * Note: This class/interface is part of an interim API that is still under
 * development and expected to change significantly before reaching stability.
 * It is being made available at this early stage to solicit feedback from pioneering
 * adopters on the understanding that any code that uses this API will almost
 * certainly be broken (repeatedly) as the API evolves.
 *

 *
 * @see MultiStatus
 * @see PropStat
 */
public class ResponseBody extends ElementEditor {
    /**
     * An ordered collection of the element names of the responsebody
     * element's children.
     */
    protected static final String[] childNames = new String[] {"href", "href", "status", "propstat", "responsedescription"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$

    /**
     * An ordered collection of the element names of the responsebody
     * element's children in the "status" form.
     */
    public static String[] fgNamesStatus = new String[] {"href", "href", "status", "responsedescription"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$

    /**
     * An ordered collection of the element names of the responsebody
     * element's children in the "propstat" form.
     */
    public static String[] fgNamesPropStat = new String[] {"href", "propstat", "responsedescription"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$

    /**
     * Creates a new editor on the given WebDAV response element. The
     * element is assumed to be well formed.
     *
     * @param root a response element
     * @throws        MalformedElementException if there is reason to
     *                believe that the element is not well formed
     */
    public ResponseBody(Element root) throws MalformedElementException {
        super(root, "response"); //$NON-NLS-1$
    }

    /**
     * Gets this response's first propstat with the given status and adds an
     * element created from the given property name as a property of the
     * propstat's prop. If such a propstat does not exists, it is created.
     * The propstat's response description is set to the given response
     * description, or removed if the response description is
     * null. Returns the propstat. The property name must not
     * be null and its qualifier and local part must not be
     * null and must not be the empty string. The status must
     * not be null. This response must not already be in the
     * "status" form.
     *
     * @param propertyName        the name of the property to create and add
     * @param status              the status of the propstat to add the
     *                            property element to
     * @param responseDescription the new response description of the
     *                            propstat, or null to remove
     *                            the old one
     * @return                    a propstat with the specified property
     *                            element, status, and response description
     */
    public PropStat accumulatePropStat(QualifiedName propertyName, String status, String responseDescription) {

        Assert.isNotNull(propertyName);
        Assert.isNotNull(status);

        Element child = getFirstChild(root, new String[] {"href", "status"}); //$NON-NLS-1$ //$NON-NLS-2$
        Assert.isTrue(child == null || isDAVElement(child, "href") //$NON-NLS-1$
                && getNextSibling(child, new String[] {"href", "status"}) == null); //$NON-NLS-1$ //$NON-NLS-2$

        String nsName = propertyName.getQualifier();
        Assert.isTrue(!"".equals(nsName)); //$NON-NLS-1$

        String localName = propertyName.getLocalName();
        Assert.isNotNull(localName);
        Assert.isTrue(!localName.equals("")); //$NON-NLS-1$

        Document document = root.getOwnerDocument();
        Element element = document.createElement(localName);
        declareNS(element, null, nsName);

        try {
            return accumulatePropStat(element, status, responseDescription);
        } catch (MalformedElementException e) {
            Assert.isTrue(false, Policy.bind("assert.internalError")); //$NON-NLS-1$
            return null; // Never reached.
        }
    }

    /**
     * Gets this response's first propstat with the given status and adds a
     * clone of the given element as a property of its prop. If such a
     * propstat does not exists, it is created. The propstat's response
     * description is set to the given response description, or removed if
     * the response description is null. Returns the propstat.
     * The element and status must not be null. This response
     * must not already be in the "status" form.
     *
     * @param element             the property element being added
     * @param status              the status of the propstat to add the
     *                            property element to
     * @param responseDescription the new response description of the
     *                            propstat, or null to remove
     *                            the old one
     * @return                    a propstat with the given property
     *                            element, status, and response description
     * @throws                    MalformedElementException if there is
     *                            reason to believe that the given element
     *                            is not well formed
     */
    public PropStat accumulatePropStat(Element element, String status, String responseDescription) throws MalformedElementException {

        Assert.isNotNull(element);
        Assert.isNotNull(status);

        Element child = getFirstChild(root, new String[] {"href", "status"}); //$NON-NLS-1$ //$NON-NLS-2$
        Assert.isTrue(child == null || isDAVElement(child, "href") //$NON-NLS-1$
                && getNextSibling(child, new String[] {"href", "status"}) == null); //$NON-NLS-1$ //$NON-NLS-2$

        boolean found = false;
        Element propstat = getFirstChild(root, "propstat"); //$NON-NLS-1$

        while (!found && propstat != null) {
            String text = getChildText(propstat, "status", false); //$NON-NLS-1$
            if (text != null && text.equals(status))
                found = true;
            else
                propstat = getTwin(propstat, true);

        }

        Element prop = null;

        if (propstat == null) {
            propstat = addChild(root, "propstat", fgNamesPropStat, false); //$NON-NLS-1$
            prop = setChild(propstat, "prop", PropStat.childNames, true); //$NON-NLS-1$
            setChild(propstat, "status", status, PropStat.childNames, false); //$NON-NLS-1$
        } else {
            prop = getFirstChild(propstat, "prop"); //$NON-NLS-1$
            if (prop == null)
                prop = setChild(propstat, "prop", PropStat.childNames, true); //$NON-NLS-1$
        }

        if (responseDescription == null) {
            Element responsedescription = getLastChild(propstat, "responsedescription"); //$NON-NLS-1$
            if (responsedescription != null)
                propstat.removeChild(responsedescription);
        } else
            setChild(propstat, "responsedescription", //$NON-NLS-1$
                    responseDescription, PropStat.childNames, false);

        extractNode(prop, element);

        try {
            return new PropStat(propstat);
        } catch (MalformedElementException e) {
            Assert.isTrue(false, Policy.bind("assert.internalError")); //$NON-NLS-1$
            return null; // Never reached.
        }
    }

    /**
     * Adds the given href to this response. If setHref(String)
     * hasn't been called and no hrefs have been added, this method sets
     * the first href and is thus equivalent to
     * setHref(String). The href must not be null.
     * This response must not already be in "propstat" form.
     *
     * @param href the href to add
     */
    public void addHref(String href) {
        Assert.isNotNull(href);
        Assert.isTrue(getLastChild(root, "propstat") == null); //$NON-NLS-1$
        addChild(root, "href", encodeHref(href), fgNamesStatus, false); //$NON-NLS-1$
    }

    /**
     * Creates and adds a propstat element on this response and returns an
     * editor on it.
     *
     * @return an editor on a propstat element
     */
    public PropStat addPropStat() {
        Element firstHref = getFirstChild(root, "href"); //$NON-NLS-1$
        Assert.isTrue(firstHref == null || getNextSibling(firstHref, new String[] {"href", "status"}) == null); //$NON-NLS-1$ //$NON-NLS-2$
        Element element = addChild(root, "propstat", fgNamesPropStat, false); //$NON-NLS-1$
        try {
            return new PropStat(element);
        } catch (MalformedElementException e) {
            Assert.isTrue(false, Policy.bind("assert.internalError")); //$NON-NLS-1$
            return null; // Never reached.
        }
    }

    /**
     * Changes all of this response's propstats with the given old status
     * to have the given new status. In addition, their response
     * descriptions are changed to be the given response description or
     * removed if the given response description is null. The
     * old status and new status must not be null</null>. This
     * response must not be in the "status" form.
     *
     * @param oldStatus           the old status of the propstat's that are
     *                            changed
     * @param newStatus           the new status of the propstat's that are
     *                            changed
     * @param responseDescription the new response description of the
     *                            propstat's that are changed, or
     *                            null</null>
     */
    public void changePropStatStatus(String oldStatus, String newStatus, String responseDescription) {
        Assert.isNotNull(oldStatus);
        Assert.isNotNull(newStatus);
        Element firstHref = getFirstChild(root, "href"); //$NON-NLS-1$
        Assert.isTrue(firstHref == null || getNextSibling(firstHref, new String[] {"href", "status"}) == null); //$NON-NLS-1$ //$NON-NLS-2$
        Element propstat = getFirstChild(root, "propstat"); //$NON-NLS-1$
        while (propstat != null) {
            String status = getChildText(propstat, "status", true); //$NON-NLS-1$
            if (oldStatus.equals(status)) {
                setChild(propstat, "status", newStatus, PropStat.childNames, true); //$NON-NLS-1$
                if (responseDescription == null) {
                    Element responsedescription = getLastChild(propstat, "responsedescription"); //$NON-NLS-1$
                    if (responsedescription != null)
                        propstat.removeChild(responsedescription);
                } else
                    setChild(propstat, "responsedescription", //$NON-NLS-1$
                            responseDescription, PropStat.childNames, false);
            }
            propstat = getTwin(propstat, true);
        }
    }

    /**
     * Creates a new WebDAV response element and sets it as the root of the
     * given document. Returns an editor on the new response element. The
     * document must not be null, and must not already have a
     * root element.
     *
     * @param document the document that will become the root of a new
     *                 response element
     * @return         an element editor on a response element
     */
    public static ResponseBody create(Document document) {
        Assert.isNotNull(document);
        Assert.isTrue(document.getDocumentElement() == null);
        Element element = create(document, "response"); //$NON-NLS-1$
        ResponseBody result = null;
        try {
            result = new ResponseBody(element);
        } catch (MalformedElementException e) {
            Assert.isTrue(false, Policy.bind("assert.internalError")); //$NON-NLS-1$
        }
        return result;
    }

    /**
     * Returns this response's first href.
     *
     * @return this response's first href.
     * @throws MalformedElementException if there is reason to believe that
     *         this editor's underlying element is not well formed
     */
    public String getHref() throws MalformedElementException {
        String href = getChildText(root, "href", true); //$NON-NLS-1$
        ensureNotNull(Policy.bind("ensure.missingHrefElmt"), href); //$NON-NLS-1$
        return decodeHref(href);
    }

    /**
     * Returns an Enumeration of this response's hrefs (not
     * including the first href).
     *
     * @return an Enumeration of this response's href
     *         Strings
     * @throws MalformedElementException if there is reason to believe that
     *         this editor's underlying element is not well formed, or if
     *         this response is not in "status" form
     */
    public Enumeration getHrefs() throws MalformedElementException {
        final Node firstHref = getFirstChild(root, "href"); //$NON-NLS-1$
        ensureNotNull(Policy.bind("ensure.missingHrefElmt"), firstHref); //$NON-NLS-1$
        ensureNotNull(Policy.bind("ensure.missingStatusElmt"), //$NON-NLS-1$
                getNextSibling((Element) firstHref, "status")); //$NON-NLS-1$
        Enumeration e = new Enumeration() {
            Node currentHref = getTwin((Element) firstHref, true);

            public boolean hasMoreElements() {
                return currentHref != null;
            }

            public Object nextElement() {
                if (!hasMoreElements())
                    throw new NoSuchElementException();
                String href = getFirstText((Element) currentHref);
                currentHref = getTwin((Element) currentHref, true);
                return decodeHref(href);
            }
        };
        return e;
    }

    /**
     * Returns an Enumeration of this response's
     * Propstats.
     *
     * @return an Enumeration of this response's
     *            PropStats
     * @throws MalformedElementException if there is reason to believe that
     *         this editor's underlying element is not well formed, or if
     *         this response is not in "propstat" form
     */
    public Enumeration getPropStats() throws MalformedElementException {
        final Element firstPropStat = getFirstChild(root, "propstat"); //$NON-NLS-1$
        ensureNotNull("ensure.missingPropstatElmt", firstPropStat); //$NON-NLS-1$
        Enumeration e = new Enumeration() {
            Element currentPropStat = firstPropStat;

            public boolean hasMoreElements() {
                return currentPropStat != null;
            }

            public Object nextElement() {
                if (!hasMoreElements())
                    throw new NoSuchElementException();
                PropStat result = null;
                try {
                    result = new PropStat(currentPropStat);
                } catch (MalformedElementException ex) {
                    Assert.isTrue(false, Policy.bind("assert.internalError")); //$NON-NLS-1$
                }
                currentPropStat = getTwin(currentPropStat, true);
                return result;
            }
        };
        return e;
    }

    /**
     * Returns this response's response description, or null if
     * it has none.
     *
     * @return this response's response description, or null
     * @throws MalformedElementException if there is reason to believe that
     *         this editor's underlying element is not well formed
     */
    public String getResponseDescription() throws MalformedElementException {
        return getChildText(root, "responsedescription", false); //$NON-NLS-1$
    }

    /**
     * Returns this response's status.
     *
     * @return this response's status
     * @throws MalformedElementException if there is reason to believe that
     *         this editor's underlying element is not well formed, or if
     *         this response is not in "status" form
     */
    public String getStatus() throws MalformedElementException {
        Element status = getFirstChild(root, "status"); //$NON-NLS-1$
        ensureNotNull(Policy.bind("ensure.missingStatusElmt"), status); //$NON-NLS-1$
        return getFirstText(status);
    }

    /**
     * Returns this response's status code.
     *
     * @return this response's status code
     * @throws MalformedElementException if there is reason to believe that
     *         this editor's underlying element is not well formed, or if
     *         this response is not in "status" form
     */
    public int getStatusCode() throws MalformedElementException {
        return new Status(getStatus()).getStatusCode();
    }

    /**
     * Returns true if this response is in "propstat" form and
     * false if it is in "status" form.
     *
     * @return a boolean indicating whether this response is in "propstat"
     *         or "status" form
     * @throws MalformedElementException if there is reason to believe that
     *         this editor's underlying element is not well formed
     */
    public boolean isPropStat() throws MalformedElementException {
        Element child = getFirstChild(root, new String[] {"status", "propstat"}); //$NON-NLS-1$ //$NON-NLS-2$
        ensureNotNull(Policy.bind("ensure.missingStatusOrPropstatElmt"), child); //$NON-NLS-1$
        boolean isPropStat = isDAVElement(child, "propstat"); //$NON-NLS-1$
        if (isPropStat)
            child = getNextSibling(child, "status"); //$NON-NLS-1$
        else
            child = getNextSibling(child, "propstat"); //$NON-NLS-1$
        ensureNull(Policy.bind("ensure.conflictingStatusOrPropstatElmt"), child); //$NON-NLS-1$
        return isPropStat;
    }

    /**
     * Returns true if this response is in "status" form and
     * false if it is in "propstat" form.
     *
     * @return a boolean indicating whether this response is in "status"
     *         or "propstat" form
     * @throws MalformedElementException if there is reason to believe that
     *         this editor's underlying element is not well formed
     */
    public boolean isStatus() throws MalformedElementException {
        return !isPropStat();
    }

    /**
     * Sets this response's first href to the given href. The href must not
     * be null.
     *
     * @param href the href to set this response's first href to
     */
    public void setHref(String href) {
        Assert.isNotNull(href);
        setChild(root, "href", href, childNames, true); //$NON-NLS-1$
    }

    /**
     * Sets this response's response description to the given value. If the
     * value is null and a response description has already
     * been set, it is removed.
     *
     * @param value a response description, or null
     */
    public void setResponseDescription(String value) {
        if (value == null) {
            Element child = getLastChild(root, "responsedescription"); //$NON-NLS-1$
            if (child != null)
                root.removeChild(child);
        } else
            setChild(root, "responsedescription", value, childNames, false); //$NON-NLS-1$
    }

    /**
     * Sets this response's response description to the given value. If the
     * value is null and a response description has already
     * been set, it is removed.
     *
     * @param value a response description, or null
     */
    public void setResponseDescription(Element value) {
        Element child = getLastChild(root, "responsedescription"); //$NON-NLS-1$
        if (child != null)
            root.removeChild(child);
        if (value == null) {
            child = setChild(root, "responsedescription", childNames, false); //$NON-NLS-1$
            child.appendChild(value);
        }
    }

    /**
     * Sets the status on this response to the given status. The status
     * must not be null. This response must not already be in
     * the "propstat" form.
     *
     * @param status the status for this response
     */
    public void setStatus(String status) {
        Assert.isNotNull(status);
        Assert.isTrue(getLastChild(root, "propstat") == null); //$NON-NLS-1$
        setChild(root, "status", status, fgNamesStatus, true); //$NON-NLS-1$
    }
}


/*******************************************************************************
 * Copyright (c) 2008, 2013 EclipseSource.
 * 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:
 *    Ralf Sternberg - initial implementation and API
 ******************************************************************************/
package com.eclipsesource.json;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import com.eclipsesource.json.JsonObject.Member;

/**
 * Represents a JSON object, i.e. an unordered set of name/value pairs, where the names are strings
 * and the values are JSON values.
 *

 * Members can be added using the add(String, ...) methods which accept instances of
 * {@link JsonValue}, strings, primitive numbers, and boolean values. To modify certain values of an
 * object, use the set(String, ...) methods. Please note that the add
 * methods are faster than set as they do not search for existing members. On the other
 * hand, the add methods do not prevent adding multiple members with the same name.
 * Duplicate names are discouraged but not prohibited by JSON.
 *

 *

 * Members can be accessed by their name using {@link #get(String)}. A list of all names can be
 * obtained from the method {@link #names()}. This class also supports iterating over the members in
 * document order using an {@link #iterator()} or an enhanced for loop:
 *

 *
 *

 * for( Member member : jsonObject ) {
 *   String name = member.getName();
 *   JsonValue value = member.getValue();
 *   ...
 * }
 *

 *

 * Even though JSON objects are unordered by definition, instances of this class preserve the order
 * of members to allow processing in document order and to guarantee a predictable output.
 *

 *

 * Note that this class is not thread-safe. If multiple threads access a
 * JsonObject instance concurrently, while at least one of these threads modifies the
 * contents of this object, access to the instance must be synchronized externally. Failure to do so
 * may lead to an inconsistent state.
 *

 *

 * This class is not supposed to be extended by clients.
 *

 */
@SuppressWarnings( "serial" )
// use default serial UID
public class JsonObject extends JsonValue implements Iterable<Member> {

  private final List<String> names;
  private final List<JsonValue> values;
  private transient HashIndexTable table;

  /**
   * Creates a new empty JsonObject.
   */
  public JsonObject() {
    names = new ArrayList<String>();
    values = new ArrayList<JsonValue>();
    table = new HashIndexTable();
  }

  /**
   * Creates a new JsonObject, initialized with the contents of the specified JSON object.
   *
   * @param object
   *          the JSON object to get the initial contents from, must not be null
   */
  public JsonObject( JsonObject object ) {
    this( object, false );
  }

  private JsonObject( JsonObject object, boolean unmodifiable ) {
    if( object == null ) {
      throw new NullPointerException( "object is null" );
    }
    if( unmodifiable ) {
      names = Collections.unmodifiableList( object.names );
      values = Collections.unmodifiableList( object.values );
    } else {
      names = new ArrayList<String>( object.names );
      values = new ArrayList<JsonValue>( object.values );
    }
    table = new HashIndexTable();
    updateHashIndex();
  }

  public void copyFrom(JsonObject object) {
		if (object == null) {
			throw new NullPointerException("object is null");
		}
		names.clear();
		names.addAll(object.names);
		values.clear();
		values.addAll(object.values);
		table = new HashIndexTable();
		updateHashIndex();
  }

  /**
   * Reads a JSON object from the given reader.
   *

   * Characters are read in chunks and buffered internally, therefore wrapping an existing reader in
   * an additional BufferedReader does not improve reading
   * performance.
   *

   *
   * @param reader
   *          the reader to read the JSON object from
   * @return the JSON object that has been read
   * @throws IOException
   *           if an I/O error occurs in the reader
   * @throws ParseException
   *           if the input is not valid JSON
   * @throws UnsupportedOperationException
   *           if the input does not contain a JSON object
   */
  public static JsonObject readFrom( Reader reader ) throws IOException {
    return JsonValue.readFrom( reader ).asObject();
  }

  /**
   * Reads a JSON object from the given string.
   *
   * @param string
   *          the string that contains the JSON object
   * @return the JSON object that has been read
   * @throws ParseException
   *           if the input is not valid JSON
   * @throws UnsupportedOperationException
   *           if the input does not contain a JSON object
   */
  public static JsonObject readFrom( String string ) {
    return JsonValue.readFrom( string ).asObject();
  }

  /**
   * Returns an unmodifiable JsonObject for the specified one. This method allows to provide
   * read-only access to a JsonObject.
   *

   * The returned JsonObject is backed by the given object and reflect changes that happen to it.
   * Attempts to modify the returned JsonObject result in an
   * UnsupportedOperationException.
   *

   *
   * @param object
   *          the JsonObject for which an unmodifiable JsonObject is to be returned
   * @return an unmodifiable view of the specified JsonObject
   */
  public static JsonObject unmodifiableObject( JsonObject object ) {
    return new JsonObject( object, true );
  }

  /**
   * Appends a new member to the end of this object, with the specified name and the JSON
   * representation of the specified int value.
   *

   * This method does not prevent duplicate names. Calling this method with a name
   * that already exists in the object will append another member with the same name. In order to
   * replace existing members, use the method set(name, value) instead. However,
   *  add is much faster than set (because it does not need to
   * search for existing members). Therefore add should be preferred when constructing new
   * objects.
   *

   *
   * @param name
   *          the name of the member to add
   * @param value
   *          the value of the member to add
   * @return the object itself, to enable method chaining
   */
  public JsonObject add( String name, int value ) {
    add( name, valueOf( value ) );
    return this;
  }

  /**
   * Appends a new member to the end of this object, with the specified name and the JSON
   * representation of the specified long value.
   *

   * This method does not prevent duplicate names. Calling this method with a name
   * that already exists in the object will append another member with the same name. In order to
   * replace existing members, use the method set(name, value) instead. However,
   *  add is much faster than set (because it does not need to
   * search for existing members). Therefore add should be preferred when constructing new
   * objects.
   *

   *
   * @param name
   *          the name of the member to add
   * @param value
   *          the value of the member to add
   * @return the object itself, to enable method chaining
   */
  public JsonObject add( String name, long value ) {
    add( name, valueOf( value ) );
    return this;
  }

  /**
   * Appends a new member to the end of this object, with the specified name and the JSON
   * representation of the specified float value.
   *

   * This method does not prevent duplicate names. Calling this method with a name
   * that already exists in the object will append another member with the same name. In order to
   * replace existing members, use the method set(name, value) instead. However,
   *  add is much faster than set (because it does not need to
   * search for existing members). Therefore add should be preferred when constructing new
   * objects.
   *

   *
   * @param name
   *          the name of the member to add
   * @param value
   *          the value of the member to add
   * @return the object itself, to enable method chaining
   */
  public JsonObject add( String name, float value ) {
    add( name, valueOf( value ) );
    return this;
  }

  /**
   * Appends a new member to the end of this object, with the specified name and the JSON
   * representation of the specified double value.
   *

   * This method does not prevent duplicate names. Calling this method with a name
   * that already exists in the object will append another member with the same name. In order to
   * replace existing members, use the method set(name, value) instead. However,
   *  add is much faster than set (because it does not need to
   * search for existing members). Therefore add should be preferred when constructing new
   * objects.
   *

   *
   * @param name
   *          the name of the member to add
   * @param value
   *          the value of the member to add
   * @return the object itself, to enable method chaining
   */
  public JsonObject add( String name, double value ) {
    add( name, valueOf( value ) );
    return this;
  }

  /**
   * Appends a new member to the end of this object, with the specified name and the JSON
   * representation of the specified boolean value.
   *

   * This method does not prevent duplicate names. Calling this method with a name
   * that already exists in the object will append another member with the same name. In order to
   * replace existing members, use the method set(name, value) instead. However,
   *  add is much faster than set (because it does not need to
   * search for existing members). Therefore add should be preferred when constructing new
   * objects.
   *

   *
   * @param name
   *          the name of the member to add
   * @param value
   *          the value of the member to add
   * @return the object itself, to enable method chaining
   */
  public JsonObject add( String name, boolean value ) {
    add( name, valueOf( value ) );
    return this;
  }

  /**
   * Appends a new member to the end of this object, with the specified name and the JSON
   * representation of the specified string.
   *

   * This method does not prevent duplicate names. Calling this method with a name
   * that already exists in the object will append another member with the same name. In order to
   * replace existing members, use the method set(name, value) instead. However,
   *  add is much faster than set (because it does not need to
   * search for existing members). Therefore add should be preferred when constructing new
   * objects.
   *

   *
   * @param name
   *          the name of the member to add
   * @param value
   *          the value of the member to add
   * @return the object itself, to enable method chaining
   */
  public JsonObject add( String name, String value ) {
    add( name, valueOf( value ) );
    return this;
  }

  /**
   * Appends a new member to the end of this object, with the specified name and the specified JSON
   * value.
   *

   * This method does not prevent duplicate names. Calling this method with a name
   * that already exists in the object will append another member with the same name. In order to
   * replace existing members, use the method set(name, value) instead. However,
   *  add is much faster than set (because it does not need to
   * search for existing members). Therefore add should be preferred when constructing new
   * objects.
   *

   *
   * @param name
   *          the name of the member to add
   * @param value
   *          the value of the member to add, must not be null
   * @return the object itself, to enable method chaining
   */
  public JsonObject add( String name, JsonValue value ) {
    if( name == null ) {
      throw new NullPointerException( "name is null" );
    }
    if( value == null ) {
      throw new NullPointerException( "value is null" );
    }
    table.add( name, names.size() );
    names.add( name );
    values.add( value );
    return this;
  }

  /**
   * Sets the value of the member with the specified name to the JSON representation of the
   * specified int value. If this object does not contain a member with this name, a
   * new member is added at the end of the object. If this object contains multiple members with
   * this name, only the last one is changed.
   *

   * This method should only be used to modify existing objects. To fill a new
   * object with members, the method add(name, value) should be preferred which is much
   * faster (as it does not need to search for existing members).
   *

   *
   * @param name
   *          the name of the member to replace
   * @param value
   *          the value to set to the member
   * @return the object itself, to enable method chaining
   */
  public JsonObject set( String name, int value ) {
    set( name, valueOf( value ) );
    return this;
  }

  /**
   * Sets the value of the member with the specified name to the JSON representation of the
   * specified long value. If this object does not contain a member with this name, a
   * new member is added at the end of the object. If this object contains multiple members with
   * this name, only the last one is changed.
   *

   * This method should only be used to modify existing objects. To fill a new
   * object with members, the method add(name, value) should be preferred which is much
   * faster (as it does not need to search for existing members).
   *

   *
   * @param name
   *          the name of the member to replace
   * @param value
   *          the value to set to the member
   * @return the object itself, to enable method chaining
   */
  public JsonObject set( String name, long value ) {
    set( name, valueOf( value ) );
    return this;
  }

  /**
   * Sets the value of the member with the specified name to the JSON representation of the
   * specified float value. If this object does not contain a member with this name, a
   * new member is added at the end of the object. If this object contains multiple members with
   * this name, only the last one is changed.
   *

   * This method should only be used to modify existing objects. To fill a new
   * object with members, the method add(name, value) should be preferred which is much
   * faster (as it does not need to search for existing members).
   *

   *
   * @param name
   *          the name of the member to add
   * @param value
   *          the value of the member to add
   * @return the object itself, to enable method chaining
   */
  public JsonObject set( String name, float value ) {
    set( name, valueOf( value ) );
    return this;
  }

  /**
   * Sets the value of the member with the specified name to the JSON representation of the
   * specified double value. If this object does not contain a member with this name, a
   * new member is added at the end of the object. If this object contains multiple members with
   * this name, only the last one is changed.
   *

   * This method should only be used to modify existing objects. To fill a new
   * object with members, the method add(name, value) should be preferred which is much
   * faster (as it does not need to search for existing members).
   *

   *
   * @param name
   *          the name of the member to add
   * @param value
   *          the value of the member to add
   * @return the object itself, to enable method chaining
   */
  public JsonObject set( String name, double value ) {
    set( name, valueOf( value ) );
    return this;
  }

  /**
   * Sets the value of the member with the specified name to the JSON representation of the
   * specified boolean value. If this object does not contain a member with this name,
   * a new member is added at the end of the object. If this object contains multiple members with
   * this name, only the last one is changed.
   *

   * This method should only be used to modify existing objects. To fill a new
   * object with members, the method add(name, value) should be preferred which is much
   * faster (as it does not need to search for existing members).
   *

   *
   * @param name
   *          the name of the member to add
   * @param value
   *          the value of the member to add
   * @return the object itself, to enable method chaining
   */
  public JsonObject set( String name, boolean value ) {
    set( name, valueOf( value ) );
    return this;
  }

  /**
   * Sets the value of the member with the specified name to the JSON representation of the
   * specified string. If this object does not contain a member with this name, a new member is
   * added at the end of the object. If this object contains multiple members with this name, only
   * the last one is changed.
   *

   * This method should only be used to modify existing objects. To fill a new
   * object with members, the method add(name, value) should be preferred which is much
   * faster (as it does not need to search for existing members).
   *

   *
   * @param name
   *          the name of the member to add
   * @param value
   *          the value of the member to add
   * @return the object itself, to enable method chaining
   */
  public JsonObject set( String name, String value ) {
    set( name, valueOf( value ) );
    return this;
  }

  public JsonObject set( String name, Object value ) {
	    set( name, valueOf( value ) );
	    return this;
	  }

  /**
   * Sets the value of the member with the specified name to the specified JSON value. If this
   * object does not contain a member with this name, a new member is added at the end of the
   * object. If this object contains multiple members with this name, only the last one is changed.
   *

   * This method should only be used to modify existing objects. To fill a new
   * object with members, the method add(name, value) should be preferred which is much
   * faster (as it does not need to search for existing members).
   *

   *
   * @param name
   *          the name of the member to add
   * @param value
   *          the value of the member to add, must not be null
   * @return the object itself, to enable method chaining
   */
  public JsonObject set( String name, JsonValue value ) {
    if( name == null ) {
      throw new NullPointerException( "name is null" );
    }
    if( value == null ) {
      throw new NullPointerException( "value is null" );
    }
    int index = indexOf( name );
    if( index != -1 ) {
      values.set( index, value );
    } else {
      table.add( name, names.size() );
      names.add( name );
      values.add( value );
    }
    return this;
  }

  /**
   * Removes a member with the specified name from this object. If this object contains multiple
   * members with the given name, only the last one is removed. If this object does not contain a
   * member with the specified name, the object is not modified.
   *
   * @param name
   *          the name of the member to remove
   * @return the object itself, to enable method chaining
   */
  public JsonObject remove( String name ) {
    if( name == null ) {
      throw new NullPointerException( "name is null" );
    }
    int index = indexOf( name );
    if( index != -1 ) {
      table.remove( index );
      names.remove( index );
      values.remove( index );
    }
    return this;
  }

  /**
   * Returns the value of the member with the specified name in this object. If this object contains
   * multiple members with the given name, this method will return the last one.
   *
   * @param name
   *          the name of the member whose value is to be returned
   * @return the value of the last member with the specified name, or null if this
   *         object does not contain a member with that name
   */
  public JsonValue get( String name ) {
    if( name == null ) {
      throw new NullPointerException( "name is null" );
    }
    int index = indexOf( name );
    return index != -1 ? values.get( index ) : null;
  }

  /**
   * Returns the number of members (i.e. name/value pairs) in this object.
   *
   * @return the number of members in this object
   */
  public int size() {
    return names.size();
  }

  /**
   * Returns true if this object contains no members.
   *
   * @return true if this object contains no members
   */
  public boolean isEmpty() {
    return names.isEmpty();
  }

  /**
   * Returns a list of the names in this object in document order. The returned list is backed by
   * this object and will reflect subsequent changes. It cannot be used to modify this object.
   * Attempts to modify the returned list will result in an exception.
   *
   * @returns a list of the names in this object
   */
  public List<String> names() {
    return Collections.unmodifiableList( names );
  }

  /**
   * Returns an iterator over the members of this object in document order. The returned iterator
   * cannot be used to modify this object.
   *
   * @return an iterator over the members of this object
   */
  public Iterator<Member> iterator() {
    final Iterator<String> namesIterator = names.iterator();
    final Iterator<JsonValue> valuesIterator = values.iterator();
    return new Iterator<JsonObject.Member>() {

      public boolean hasNext() {
        return namesIterator.hasNext();
      }

      public Member next() {
        String name = namesIterator.next();
        JsonValue value = valuesIterator.next();
        return new Member( name, value );
      }

      public void remove() {
        throw new UnsupportedOperationException();
      }

    };
  }

  @Override
  protected void write( JsonWriter writer ) throws IOException {
    writer.writeObject( this );
  }

  @Override
  public boolean isObject() {
    return true;
  }

  @Override
  public JsonObject asObject() {
    return this;
  }

  @Override
  public int hashCode() {
    int result = 1;
    result = 31 * result + names.hashCode();
    result = 31 * result + values.hashCode();
    return result;
  }

  @Override
  public boolean equals( Object obj ) {
    if( this == obj ) {
      return true;
    }
    if( obj == null ) {
      return false;
    }
    if( getClass() != obj.getClass() ) {
      return false;
    }
    JsonObject other = (JsonObject)obj;
    return names.equals( other.names ) && values.equals( other.values );
  }

  int indexOf( String name ) {
    int index = table.get( name );
    if( index != -1 && name.equals( names.get( index ) ) ) {
      return index;
    }
    return names.lastIndexOf( name );
  }

  private synchronized void readObject( ObjectInputStream inputStream ) throws IOException,
      ClassNotFoundException
  {
    inputStream.defaultReadObject();
    table = new HashIndexTable();
    updateHashIndex();
  }

  private void updateHashIndex() {
    int size = names.size();
    for( int i = 0; i < size; i++ ) {
      table.add( names.get( i ), i );
    }
  }

  /**
   * Represents a member of a JSON object, i.e. a pair of name and value.
   */
  public static class Member {

    private final String name;
    private final JsonValue value;

    Member( String name, JsonValue value ) {
      this.name = name;
      this.value = value;
    }

    /**
     * Returns the name of this member.
     *
     * @return the name of this member, never null
     */
    public String getName() {
      return name;
    }

    /**
     * Returns the value of this member.
     *
     * @return the value of this member, never null
     */
    public JsonValue getValue() {
      return value;
    }

    @Override
    public int hashCode() {
      int result = 1;
      result = 31 * result + name.hashCode();
      result = 31 * result + value.hashCode();
      return result;
    }

    @Override
    public boolean equals( Object obj ) {
      if( this == obj ) {
        return true;
      }
      if( obj == null ) {
        return false;
      }
      if( getClass() != obj.getClass() ) {
        return false;
      }
      Member other = (Member)obj;
      return name.equals( other.name ) && value.equals( other.value );
    }

  }

  static class HashIndexTable {

    private final byte[] hashTable = new byte[32]; // must be a power of two

    public HashIndexTable() {
    }

    public HashIndexTable( HashIndexTable original ) {
      System.arraycopy( original.hashTable, 0, hashTable, 0, hashTable.length );
    }

    void add( String name, int index ) {
      int slot = hashSlotFor( name );
      if( index < 0xff ) {
        // increment by 1, 0 stands for empty
        hashTable[slot] = (byte)( index + 1 );
      } else {
        hashTable[slot] = 0;
      }
    }

    void remove( int index ) {
      for( int i = 0; i < hashTable.length; i++ ) {
        if( hashTable[i] == index + 1 ) {
          hashTable[i] = 0;
        } else if( hashTable[i] > index + 1 ) {
          hashTable[i]--;
        }
      }
    }

    int get( Object name ) {
      int slot = hashSlotFor( name );
      // subtract 1, 0 stands for empty
      return ( hashTable[slot] & 0xff ) - 1;
    }

    private int hashSlotFor( Object element ) {
      return element.hashCode() & hashTable.length - 1;
    }

  }

}


/*
 * Copyright 2015 Red Hat, Inc. and/or its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 *
 *      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.eclipse.webdav.internal.authentication;

import java.io.UnsupportedEncodingException;
import java.net.Authenticator;
import java.net.URL;
import java.util.Map;
import org.eclipse.webdav.http.client.IAuthenticator;
import org.eclipse.webdav.http.client.Request;
import org.eclipse.webdav.internal.kernel.utils.Assert;

/**
 * The BasicAuthority provides the necessary behavior to
 * authorizes client Request</codes>s for communication with HTTP
 * servers using the Basic authentication scheme.
 *
 * @see AuthorizationAuthority
 */
public class BasicAuthority extends AuthorizationAuthority {

    /**
     * Creates a new authenticator that stores its authentication information
     * in the given authentication store.
     *
The BasicAuthenticator authenticates according to the
     * "Basic" authentication scheme.
     *
Instances of this class must not be created directly, instead, use
     * an instance of the class Authenticator to authorize
     * requests.
     *
     * @param authenticationStore a store that holds authentication
     * information
     */
    public BasicAuthority(IAuthenticator authenticationStore) {
        super(authenticationStore);
    }

    /**
     * Returns the Basic authorization credentials for the given username and
     * password. The credentials have the following form:
     *
     * credentials             = "Basic" basic-credentials
     * basic-credentials    = base64-user-pass
     * base64-user-pass        = <base64 encoding of user-pass, except not
     *                           limited to 76 char/line>
     * user-pass            = userid ":" password
     * userid                = *<TEXT excluding ":">
     * password                = *TEXT
     *
     *
Userids might be case sensitive.
     *
For example, if the user's name is "Aladdin" and the user's
     * password is "open sesame", the following credentials are supplied:
     *
     * Basic QWxhZGRpbjpvcGVuIHN1c2FtZQ==
     *
     * @param username
     * @param password
     * @return         the Basic authorization credentials for the given
     *                 username and password
     */
    private String credentials(String username, String password) {
        Assert.isNotNull(username);
        Assert.isNotNull(password);

        String userpass = username + ":" + password; //$NON-NLS-1$
        byte[] data = null;

        try {
            data = userpass.getBytes("UTF8"); //$NON-NLS-1$
        } catch (UnsupportedEncodingException e) {
            data = userpass.getBytes();
        }

        return "Basic " + Base64Encoder.encode(data); //$NON-NLS-1$
    }

    /**
     * @see Authenticator#getAuthenticationInfo(AuthenticateChallenge, Map, URL, URL)
     */
    protected Map getAuthenticationInfo(AuthenticateChallenge challenge, Map oldInfo, URL serverUrl, URL protectionSpaceUrl) {
        Assert.isNotNull(challenge);
        Assert.isNotNull(serverUrl);
        Assert.isNotNull(protectionSpaceUrl);

        return authenticatorStore.requestAuthenticationInfo(protectionSpaceUrl, challenge.getRealm(), challenge.getAuthScheme());
    }

    /**
     * @see Authenticator#getAuthorization(Request, Map, URL, URL, URL)
     */
    protected String getAuthorization(Request request, Map info, URL serverUrl, URL protectionSpaceUrl, URL proxyServerUrl) {
        Assert.isNotNull(request);
        Assert.isNotNull(info);
        Assert.isNotNull(serverUrl);
        Assert.isNotNull(protectionSpaceUrl);

        String username = (String) info.get("username"); //$NON-NLS-1$
        String password = (String) info.get("password"); //$NON-NLS-1$

        if (username == null || password == null) {
            return null;
        }
        return credentials(username, password);
    }
}


/*
 * Copyright 2010 Red Hat, Inc. and/or its affiliates.
 *
 * 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.drools.eclipse.editors.rete.model;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;

/**
 * Abstract prototype of a model element.
 */
public abstract class ModelElement {

    /** Delegate used to implemenent property-change-support. */
    private transient PropertyChangeSupport pcsDelegate = new PropertyChangeSupport( this );

    /**
     * Attach a non-null PropertyChangeListener to this object.
     *
     * @param l a non-null PropertyChangeListener instance
     * @throws IllegalArgumentException if the parameter is null
     */
    public synchronized void addPropertyChangeListener(PropertyChangeListener l) {
        if ( l == null ) {
            throw new IllegalArgumentException();
        }
        pcsDelegate.addPropertyChangeListener( l );
    }

    /**
     * Report a property change to registered listeners (for example edit parts).
     *
     * @param property the programmatic name of the property that changed
     * @param oldValue the old value of this property
     * @param newValue the new value of this property
     */
    protected void firePropertyChange(String property,
                                      Object oldValue,
                                      Object newValue) {
        if ( pcsDelegate.hasListeners( property ) ) {
            pcsDelegate.firePropertyChange( property,
                                            oldValue,
                                            newValue );
        }
    }

    /**
     * Remove a PropertyChangeListener from this component.
     *
     * @param l a PropertyChangeListener instance
     */
    public synchronized void removePropertyChangeListener(PropertyChangeListener l) {
        if ( l != null ) {
            pcsDelegate.removePropertyChangeListener( l );
        }
    }

}


/*
 * Copyright 2010 Red Hat, Inc. and/or its affiliates.
 *
 * 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.drools.eclipse.debug;

import org.drools.eclipse.DroolsEclipsePlugin;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.IStatusHandler;
import org.eclipse.debug.core.model.IDebugTarget;
import org.eclipse.debug.core.model.ISourceLocator;
import org.eclipse.debug.core.model.IThread;
import org.eclipse.debug.core.model.IValue;
import org.eclipse.debug.core.sourcelookup.ISourceLookupDirector;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.debug.core.IJavaClassType;
import org.eclipse.jdt.debug.core.IJavaDebugTarget;
import org.eclipse.jdt.debug.core.IJavaObject;
import org.eclipse.jdt.debug.core.IJavaReferenceType;
import org.eclipse.jdt.debug.core.IJavaStackFrame;
import org.eclipse.jdt.debug.core.IJavaThread;
import org.eclipse.jdt.debug.core.IJavaType;
import org.eclipse.jdt.debug.core.IJavaValue;
import org.eclipse.jdt.debug.eval.IAstEvaluationEngine;
import org.eclipse.jdt.debug.eval.ICompiledExpression;
import org.eclipse.jdt.debug.eval.IEvaluationListener;
import org.eclipse.jdt.debug.eval.IEvaluationResult;
import org.eclipse.jdt.internal.debug.core.JDIDebugPlugin;

public class DebugUtil {

    public static final int INFO_EVALUATION_STACK_FRAME = 111;
    private static IStatus fgNeedStackFrame = new Status(IStatus.INFO,
        DroolsEclipsePlugin.getUniqueIdentifier(), INFO_EVALUATION_STACK_FRAME,
        "Provides thread context for an evaluation", null);
    private static IStatusHandler fgStackFrameProvider;

    public static IValue getValueByExpression(String expression, IValue value) {
        if (!(value instanceof IJavaObject)) {
            return null;
        }
        IJavaObject javaValue = (IJavaObject) value;
        try {
            IJavaType type = javaValue.getJavaType();
            if (!(type instanceof IJavaClassType)) {
                return null;
            }
            IJavaStackFrame stackFrame = getStackFrame(javaValue);
            if (stackFrame == null) {
                return null;
            }

            // find the project the snippets will be compiled in.
            ISourceLocator locator= javaValue.getLaunch().getSourceLocator();
            Object sourceElement= null;
            if (locator instanceof ISourceLookupDirector) {
                String[] sourcePaths = ((IJavaClassType) type).getSourcePaths(null);
                if (sourcePaths != null && sourcePaths.length > 0) {
                    sourceElement = ((ISourceLookupDirector) locator).getSourceElement(sourcePaths[0]);
                }
                if (!(sourceElement instanceof IJavaElement) && sourceElement instanceof IAdaptable) {
                    sourceElement = ((IAdaptable) sourceElement).getAdapter(IJavaElement.class);
                }
            }
            if (sourceElement == null) {
                sourceElement = locator.getSourceElement(stackFrame);
                if (!(sourceElement instanceof IJavaElement) && sourceElement instanceof IAdaptable) {
                    Object newSourceElement = ((IAdaptable) sourceElement).getAdapter(IJavaElement.class);
                    // if the source is a drl during the execution of the rule
                    if (newSourceElement != null) {
                        sourceElement = newSourceElement;
                    }
                }
            }
            IJavaProject project = null;
            if (sourceElement instanceof IJavaElement) {
                project = ((IJavaElement) sourceElement).getJavaProject();
            } else if (sourceElement instanceof IResource) {
                IJavaProject resourceProject = JavaCore.create(((IResource) sourceElement).getProject());
                if (resourceProject.exists()) {
                    project = resourceProject;
                }
            }
            if (project == null) {
                return null;
            }

            IAstEvaluationEngine evaluationEngine = JDIDebugPlugin.getDefault()
                    .getEvaluationEngine(project,
                            (IJavaDebugTarget) stackFrame.getDebugTarget());

            EvaluationBlock evaluationBlock = new EvaluationBlock(javaValue,
                    (IJavaReferenceType) type, (IJavaThread) stackFrame.getThread(),
                    evaluationEngine);
            return evaluationBlock.evaluate(expression);

        } catch (CoreException e) {
            throw new RuntimeException("Could not evaluate expression", e);
        }
    }

    /**
     * Return the current stack frame context, or a valid stack frame for the
     * given value.
     */
    public static IJavaStackFrame getStackFrame(IValue value)
            throws CoreException {
        IStatusHandler handler = getStackFrameProvider();
        if (handler != null) {
            IJavaStackFrame stackFrame = (IJavaStackFrame) handler
                    .handleStatus(fgNeedStackFrame, value);
            if (stackFrame != null) {
                return stackFrame;
            }
        }
        IDebugTarget target = value.getDebugTarget();
        IJavaDebugTarget javaTarget = (IJavaDebugTarget) target
                .getAdapter(IJavaDebugTarget.class);
        if (javaTarget != null) {
            IThread[] threads = javaTarget.getThreads();
            for (int i = 0; i < threads.length; i++) {
                IThread thread = threads[i];
                if (thread.isSuspended()) {
                    return (IJavaStackFrame) thread.getTopStackFrame();
                }
            }
        }
        return null;
    }

    private static IStatusHandler getStackFrameProvider() {
        if (fgStackFrameProvider == null) {
            fgStackFrameProvider = DebugPlugin.getDefault().getStatusHandler(
                    fgNeedStackFrame);
        }
        return fgStackFrameProvider;
    }

    private static class EvaluationBlock implements IEvaluationListener {

        private IJavaObject fEvaluationValue;
        private IJavaReferenceType fEvaluationType;
        private volatile IJavaThread fThread;
        private IAstEvaluationEngine fEvaluationEngine;
        private volatile IEvaluationResult fResult;

        public EvaluationBlock(IJavaObject value, IJavaReferenceType type,
                IJavaThread thread, IAstEvaluationEngine evaluationEngine) {
            fEvaluationValue = value;
            fEvaluationType = type;
            fThread = thread;
            fEvaluationEngine = evaluationEngine;
        }

        public void evaluationComplete(IEvaluationResult result) {
            synchronized ( fThread ) {
                fResult = result;
                fThread.notifyAll();
            }
        }

        public IJavaValue evaluate(String snippet) throws DebugException {
            ICompiledExpression compiledExpression = fEvaluationEngine
                    .getCompiledExpression(snippet, fEvaluationType);
            if (compiledExpression.hasErrors()) {
                String[] errorMessages = compiledExpression.getErrorMessages();
                String message = "";

                for ( int i = 0; i < errorMessages.length; i++ ) {
                    message += errorMessages[i] + "\n";
                }
                throw new DebugException(new Status(IStatus.ERROR,
                    DroolsEclipsePlugin.PLUGIN_ID, DroolsEclipsePlugin.INTERNAL_ERROR,
                    "Error when compiling snippet " + snippet + ": " + message, null));
            }
            fResult = null;
            fEvaluationEngine.evaluateExpression(compiledExpression,
                    fEvaluationValue, fThread, this,
                    DebugEvent.EVALUATION_IMPLICIT, false);

            synchronized ( fThread ) {
                while ( fResult == null ) {
                        try {
                            fThread.wait( 100 );
                        } catch ( InterruptedException e ) {
                        }
                }
            }

            if (fResult == null) {
                return null;
            }
            if (fResult.hasErrors()) {
                return null;
            }
            return fResult.getValue();
        }
    }

}