/*******************************************************************************
 * Copyright (c) 2001, 2009 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     Jens Lukowski/Innoopract - initial renaming/restructuring
 *     
 *******************************************************************************/
package org.eclipse.wst.xml.core.internal.document;



import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * An array-backed NodeList, used to keep the array size down
 */
public class NodeListImpl implements NodeList {

	private Object fLockObject = new byte[0];
	private final static int growthConstant = 2;
	private Node[] fNodes = null;
	private int fNodeCount = 0;

	/**
	 * NodeListImpl constructor
	 */
	public NodeListImpl() {
		super();
	}

	protected NodeListImpl(NodeListImpl list) {
		super();
		fNodeCount = list.fNodeCount;
		fNodes = list.fNodes;
	}

	/**
	 * appendNode method
	 * 
	 * @return org.w3c.dom.Node
	 * @param node
	 *            org.w3c.dom.Node
	 */
	protected Node appendNode(Node node) {
		if (node == null)
			return null;
		synchronized (fLockObject) {
			ensureCapacity(fNodeCount + 1);
			fNodes[fNodeCount++] = node;
		}
		return node;
	}

	/**
	 * Grow the node array to at least the given size while keeping the
	 * contents the same
	 * 
	 * @param needed
	 */
	private void ensureCapacity(int needed) {
		if (fNodes == null) {
			// first time
			fNodes = new Node[needed];
			return;
		}
		int oldLength = fNodes.length;
		if (oldLength < needed) {
			Node[] oldNodes = fNodes;
			Node[] newNodes = new Node[needed + growthConstant];
			System.arraycopy(oldNodes, 0, newNodes, 0, fNodeCount);
			fNodes = newNodes;
		}
	}

	/* (non-Javadoc)
	 * @see org.w3c.dom.NodeList#getLength()
	 */
	public int getLength() {
		return fNodeCount;
	}
	
	protected final Node[] getNodes() {
		return fNodes;
	}

	/**
	 */
	protected Node insertNode(Node node, int index) {
		if (node == null)
			return null;
		synchronized (fLockObject) {
			if (fNodes == null) {
				// first time, ignore index
				fNodes = new Node[1];
				fNodes[fNodeCount++] = node;
				return node;
			}

			// gracefully handle out of bounds
			if (index < 0 || index > fNodeCount + 1)
				return appendNode(node);

			/*
			 * Grow a new Node array, copying the old contents around the new
			 * Node
			 */
			Node[] newNodes = new Node[fNodeCount + growthConstant];
			System.arraycopy(fNodes, 0, newNodes, 0, index);
			newNodes[index] = node;
			System.arraycopy(fNodes, index, newNodes, index + 1, fNodeCount - index);
			fNodes = newNodes;
			fNodeCount++;
		}
		return node;
	}

	/* (non-Javadoc)
	 * @see org.w3c.dom.NodeList#item(int)
	 */
	public Node item(int index) {
		if (index < 0 || index >= fNodeCount)
			return null; // invalid parameter
		return fNodes[index];
	}

	/**
	 * removeNode method
	 * 
	 * @return org.w3c.dom.Node
	 * @param index
	 *            int
	 */
	protected Node removeNode(int index) {
		if (this.fNodes == null)
			return null; // no node

		synchronized (fLockObject) {
			if (index < 0 || index >= fNodeCount)
				return null; // invalid parameter

			Node removed = fNodes[index];
			Node[] newNodes = new Node[fNodeCount - 1];
			// copy around node being removed
			System.arraycopy(fNodes, 0, newNodes, 0, index);
			System.arraycopy(fNodes, index + 1, newNodes, index, fNodeCount - index - 1);
			fNodes = newNodes;
			fNodeCount--;
			return removed;
		}
	}
}
