/*
 ***** BEGIN LICENSE BLOCK *****
 * Version: EPL 2.0/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Eclipse Public
 * 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.eclipse.org/legal/epl-v20.html
 *
 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.
 *
 * Copyright (C) 2004-2005 Thomas E Enebo <enebo@acm.org>
 * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
 * 
 * Alternatively, the contents of this file may be used under the terms of
 * either of the GNU General Public License Version 2 or later (the "GPL"),
 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the EPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the EPL, the GPL or the LGPL.
 ***** END LICENSE BLOCK *****/

package org.jruby.ast;

import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.jruby.ast.visitor.NodeVisitor;
import org.jruby.lexer.yacc.ISourcePosition;

All Nodes which have a list representation inherit this. This is also used as generic container for additional information that is not directly evaluated. In particular, f_arg production rule uses this to capture arg information for the editor projects who want position info saved.
/** * All Nodes which have a list representation inherit this. This is also used * as generic container for additional information that is not directly evaluated. * In particular, f_arg production rule uses this to capture arg information for * the editor projects who want position info saved. */
public class ListNode extends Node implements Iterable<Node> { private static final Node[] EMPTY = new Node[0]; private static final int INITIAL_SIZE = 4; private Node[] list = EMPTY; private int size = 0;
This is used to reduce the need to allocate an array for the many single-element ListNode instances in a typical Ruby app. The methods below detect if single has been set and act accordingly, expanding it to an array lazily only when needed.
/** * This is used to reduce the need to allocate an array for the many single-element ListNode instances * in a typical Ruby app. The methods below detect if single has been set and act accordingly, expanding * it to an array lazily only when needed. */
private Node single;
Create a new ListNode.
Params:
  • position – where list is
  • firstNode – first element of the list
/** * Create a new ListNode. * * @param position where list is * @param firstNode first element of the list */
public ListNode(ISourcePosition position, Node firstNode) { super(position, firstNode != null && firstNode.containsVariableAssignment); single = firstNode; size = 1; } public ListNode(ISourcePosition position) { super(position, false); } public NodeType getNodeType() { return NodeType.LISTNODE; } private Node[] growList(int mustBeDelta) { int newSize = list.length * 2; // Fairly arbitrary to scale 1.5 here but this means we are adding a lot so I think // we can taper the multiplier if (size + mustBeDelta >= newSize) newSize = (int) ((size + mustBeDelta) * 1.5); Node[] newList = new Node[newSize]; System.arraycopy(list, 0, newList, 0, size); return list = newList; } protected void addInternal(Node node) { Node single = this.single; if (single != null) { addToSingle(this.single, node); return; } if (size >= list.length) growList(1); list[size++] = node; } protected void addAllInternal(ListNode other) { if (other.size == 0) return; Node[] list; Node single = this.single; if (single != null) { list = arrayifySingle(single); } else { list = this.list; } if (size + other.size() >= list.length) list = growList(other.size); Node otherSingle = other.single; if (otherSingle != null) { list[size++] = otherSingle; return; } System.arraycopy(other.list, 0, list, size, other.size); size += other.size; } private void addToSingle(Node single, Node node) { arrayifySingle(single)[size++] = node; } private Node[] arrayifySingle(Node single) { Node[] list = new Node[INITIAL_SIZE]; list[0] = single; this.list = list; this.single = null; return list; } public ListNode add(Node node) { // Ruby Grammar productions return plenty of nulls. if (node == null || node == NilImplicitNode.NIL) { addInternal(NilImplicitNode.NIL); return this; } if (node.containsVariableAssignment()) containsVariableAssignment = true; addInternal(node); if (getPosition() == null) setPosition(node.getPosition()); return this; } public int size() { return size; }
Add all elements in other list to this list node.
Params:
  • other – list which has elements
Returns:this instance for method chaining
/** * Add all elements in other list to this list node. * * @param other list which has elements * @return this instance for method chaining */
public ListNode addAll(ListNode other) { if (other != null && other.size() > 0) { if (other.containsVariableAssignment()) containsVariableAssignment = true; addAllInternal(other); if (getPosition() == null) setPosition(other.getPosition()); } return this; } public ListNode addAll(Node[] other, int index, int length) { Node single = this.single; if (single != null) { list = new Node[length + 1]; list[0] = single; this.single = null; System.arraycopy(other, index, list, 1, length); } if (size + length < list.length) { growList(length); } for (int i = 0; i < length; i++) { addInternal(other[index + i]); } return this; }
Add other element to this list
Params:
  • other – list which has elements
Returns:this instance for method chaining
/** * Add other element to this list * * @param other list which has elements * @return this instance for method chaining */
public ListNode addAll(Node other) { return add(other); } public Node getLast() { int size = this.size; if (size == 0) return null; return get(size - 1); } public boolean isEmpty() { return size == 0; } public Node[] children() { Node single = this.single; if (single != null) return new Node[] {single}; Node[] properList = new Node[size]; System.arraycopy(list, 0, properList, 0, size); return properList; } @Deprecated public List<Node> childNodes() { Node single = this.single; if (single != null) return Collections.singletonList(single); return Arrays.asList(children()); } public <T> T accept(NodeVisitor<T> visitor) { return visitor.visitListNode(this); } public Node get(int idx) { Node single = this.single; if (idx == 0 && single != null) return single; return list[idx]; } @Override public Iterator<Node> iterator() { return new Iterator<Node>() { int i = 0; @Override public boolean hasNext() { return i < list.length; } @Override public Node next() { if (i >= list.length) throw new IndexOutOfBoundsException(String.valueOf(i)); return list[i++]; } @Override public void remove() { throw new UnsupportedOperationException("remove"); } }; } }