/* * Copyright 2003-2013 JetBrains s.r.o. * * 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 jetbrains.mps.idea.core.psi.impl; import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; /** * Created with IntelliJ IDEA. * User: fyodor * Date: 2/14/13 * Time: 12:53 PM * To change this template use File | Settings | File Templates. */ /*package*/ class NodeList { private MPSPsiNodeBase owner; private Entry head; private int size = 0; NodeList(MPSPsiNodeBase owner) { this.owner = owner; } MPSPsiNodeBase owner() { return owner; } MPSPsiNodeBase first() { return head != null ? head.node : null; } MPSPsiNodeBase last() { return head != null && head.prev != null ? head.prev.node : null; } MPSPsiNodeBase next(MPSPsiNodeBase prev) { assert prev.getEntry().list() == this; return prev.getEntry().next.node; } MPSPsiNodeBase prev(MPSPsiNodeBase prev) { assert prev.getEntry().list() == this; return prev.getEntry().prev.node; } int size () { return size; } void toArray(PsiElement[] array) { Entry e = head; if (e == null) return; int i = 0; do { array[i++] = e.node; e = e.next; } while (e != head); } void addFirst (@NotNull MPSPsiNodeBase node) { assert node.getEntry() == null; Entry newHead = new Entry(node); if (head == null) { head = newHead; head.next = head.prev = head; } else { insertBetween(newHead, head.prev, head); head = newHead; } size++; } void addLast (@NotNull MPSPsiNodeBase node) { assert node.getEntry() == null; Entry newTail = new Entry(node); if (head == null) { head = newTail; head.next = head.prev = head; } else { insertBetween(newTail, head.prev, head); } size++; } void insertAfter(@NotNull MPSPsiNodeBase anchor, @NotNull MPSPsiNodeBase node) { assert anchor != null && anchor.getEntry() != null && anchor.getEntry().list() == this; assert node.getEntry() == null; Entry toInsert = new Entry(node); insertBetween(toInsert, anchor.getEntry(), anchor.getEntry().next); size++; } void insertBefore(@NotNull MPSPsiNodeBase anchor, @NotNull MPSPsiNodeBase node) { assert anchor.getEntry() != null && anchor.getEntry().list() == this; assert node.getEntry() == null; Entry toInsert = new Entry(node); insertBetween(toInsert, anchor.getEntry().prev, anchor.getEntry()); size++; } void remove (@NotNull MPSPsiNodeBase node) { assert node.getEntry().list() == this; Entry toRemove = node.getEntry(); if (toRemove.next != toRemove) { toRemove.next.prev = toRemove.prev; toRemove.prev.next = toRemove.next; if (head == toRemove) { head = toRemove.next; } } else { assert head == toRemove; head = null; } toRemove.clear(); size--; } void replace (@NotNull MPSPsiNodeBase old, @NotNull MPSPsiNodeBase replacement) { assert old.getEntry().list() == this; assert replacement.getEntry() == null; Entry entry = old.getEntry(); old.setEntry(null); replacement.setEntry(entry); entry.node = replacement; } private void insertBetween(Entry toInsert, Entry after, Entry before) { toInsert.prev = after; toInsert.next = before; after.next = toInsert; before.prev = toInsert; } void clear () { Entry e = head; if (e == null) return; do { Entry next = e.next; e.clear(); e = next; } while (e != head); head = null; size = 0; } Entry findEntry(@NotNull MPSPsiNodeBase node) { Entry e = head; if (e == null) return null; do { Entry next = e.next; if(e.node.equals(node)) return e; e = next; } while (e != head); return null; } class Entry { private Entry next; private Entry prev; private MPSPsiNodeBase node; Entry (MPSPsiNodeBase node) { this.node = node; node.setEntry(this); } void clear () { node.setEntry(null); node = null; next = null; prev = null; } public NodeList list() { return NodeList.this; } boolean isFirst() { return NodeList.this.head == this; } boolean isLast () { return NodeList.this.head != null && NodeList.this.head.prev == this; } boolean isAttached () { return NodeList.this.owner != null; } } }