| /******************************************************************************* |
| * Copyright (c) 2011 NumberFour AG |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0. |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * NumberFour AG - initial API and Implementation (Alex Panchenko) |
| *******************************************************************************/ |
| package org.eclipse.dltk.javascript.typeinfo; |
| |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.NoSuchElementException; |
| |
| import org.eclipse.dltk.javascript.core.Types; |
| import org.eclipse.dltk.javascript.typeinfo.model.JSType; |
| import org.eclipse.dltk.javascript.typeinfo.model.Type; |
| |
| public abstract class JSTypeSet implements Iterable<IRType> { |
| |
| private static class EmptyIterator implements Iterator<IRType> { |
| |
| protected EmptyIterator() { |
| } |
| |
| public boolean hasNext() { |
| return false; |
| } |
| |
| public IRType next() { |
| throw new NoSuchElementException(); |
| } |
| |
| public void remove() { |
| throw new UnsupportedOperationException(); |
| } |
| } |
| |
| private static class JSEmptyTypeSet extends JSTypeSet { |
| |
| protected JSEmptyTypeSet() { |
| } |
| |
| public Iterator<IRType> iterator() { |
| return new EmptyIterator(); |
| } |
| |
| @Override |
| public void add(IRType type) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public IRType getFirst() { |
| return null; |
| } |
| |
| @Override |
| public IRType toRType() { |
| return null; |
| } |
| |
| @Override |
| public Type[] toArray() { |
| return new Type[0]; |
| } |
| |
| @Override |
| public int size() { |
| return 0; |
| } |
| |
| @Override |
| public void addAll(Iterable<IRType> types) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public boolean isEmpty() { |
| return true; |
| } |
| |
| @Override |
| public void clear() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public boolean contains(IRType type) { |
| return false; |
| } |
| |
| @Override |
| public boolean containsAll(JSTypeSet types) { |
| return types.isEmpty(); |
| } |
| |
| @Override |
| public String toString() { |
| return getClass().getSimpleName(); |
| } |
| |
| } |
| |
| private static final JSEmptyTypeSet EMPTY_SET = new JSEmptyTypeSet(); |
| |
| public static JSTypeSet emptySet() { |
| return EMPTY_SET; |
| } |
| |
| private static class SingletonIterator implements Iterator<IRType> { |
| private final IRType type; |
| private boolean hasNext = true; |
| |
| public SingletonIterator(IRType type) { |
| this.type = type; |
| } |
| |
| public boolean hasNext() { |
| return hasNext; |
| } |
| |
| public IRType next() { |
| if (hasNext) { |
| hasNext = false; |
| return type; |
| } |
| throw new NoSuchElementException(); |
| } |
| |
| public void remove() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| } |
| |
| private static class JSSingletonTypeSet extends JSTypeSet { |
| |
| private final IRType type; |
| |
| public JSSingletonTypeSet(IRType type) { |
| assert type != null; |
| this.type = type; |
| } |
| |
| public Iterator<IRType> iterator() { |
| return new SingletonIterator(type); |
| } |
| |
| @Override |
| public void add(IRType type) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public IRType getFirst() { |
| return type; |
| } |
| |
| @Override |
| public IRType toRType() { |
| return type; |
| } |
| |
| @Override |
| public Type[] toArray() { |
| if (type instanceof IRSimpleType) { |
| return new Type[] { ((IRSimpleType) type).getTarget() }; |
| } else if (type instanceof IRClassType) { |
| return new Type[] { ((IRClassType) type).getTarget() }; |
| } else if (type == RTypes.any()) { |
| return new Type[] { Types.OBJECT }; |
| } else { |
| return new Type[0]; |
| } |
| } |
| |
| @Override |
| public int size() { |
| return 1; |
| } |
| |
| @Override |
| public void addAll(Iterable<IRType> types) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public boolean isEmpty() { |
| return false; |
| } |
| |
| @Override |
| public void clear() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public boolean contains(IRType type) { |
| return this.type.equals(type); |
| } |
| |
| @Override |
| public boolean containsAll(JSTypeSet types) { |
| return types.isEmpty() |
| || (types.size() == 1 && contains(types.toRType())); |
| } |
| |
| @Override |
| public String toString() { |
| return "[" + type + "]"; |
| } |
| |
| } |
| |
| public static JSTypeSet singleton(IRType type) { |
| if (type instanceof IRUnionType) { |
| final JSTypeSet set = create(); |
| set.add(type); |
| return set; |
| } |
| return new JSSingletonTypeSet(type); |
| } |
| |
| /** |
| * @deprecated Use {@link RTypes#create(ITypeSystem,JSType)} instead |
| */ |
| public static IRType normalize(ITypeSystem context, JSType type) { |
| return RTypes.create(context, type); |
| } |
| |
| /** |
| * @deprecated Use {@link RTypes#any()} instead |
| */ |
| public static IRType any() { |
| return RTypes.any(); |
| } |
| |
| /** |
| * @deprecated Use {@link RTypes#none()} instead |
| */ |
| public static IRType none() { |
| return RTypes.none(); |
| } |
| |
| /** |
| * @deprecated Use {@link RTypes#undefined()} instead |
| */ |
| public static IRType undefined() { |
| return RTypes.undefined(); |
| } |
| |
| private static class JSTypeSetImpl extends JSTypeSet { |
| |
| private final List<IRType> types = new ArrayList<IRType>(3); |
| |
| protected JSTypeSetImpl() { |
| } |
| |
| public Iterator<IRType> iterator() { |
| return types.iterator(); |
| } |
| |
| @Override |
| public void add(IRType type) { |
| assert type != null; |
| if (type instanceof IRUnionType) { |
| for (IRType t : ((IRUnionType) type).getTargets()) { |
| add(t); |
| } |
| } else { |
| if (!types.contains(type)) { |
| types.add(type); |
| } |
| } |
| } |
| |
| @Override |
| public IRType getFirst() { |
| return types.get(0); |
| } |
| |
| @Override |
| public IRType toRType() { |
| if (types.isEmpty()) { |
| return null; |
| } else if (types.size() == 1) { |
| return types.get(0); |
| } else { |
| return RTypes.union(types); |
| } |
| } |
| |
| @Override |
| public Type[] toArray() { |
| final LinkedHashSet<Type> result = new LinkedHashSet<Type>(); |
| for (IRType type : types) { |
| if (type instanceof IRSimpleType) { |
| result.add(((IRSimpleType) type).getTarget()); |
| } else if (type == RTypes.any()) { |
| result.add(Types.OBJECT); |
| } |
| } |
| return result.toArray(new Type[result.size()]); |
| } |
| |
| @Override |
| public int size() { |
| return types.size(); |
| } |
| |
| @Override |
| public void addAll(Iterable<IRType> types) { |
| for (IRType type : types) { |
| add(type); |
| } |
| } |
| |
| @Override |
| public boolean isEmpty() { |
| return types.isEmpty(); |
| } |
| |
| @Override |
| public void clear() { |
| types.clear(); |
| } |
| |
| @Override |
| public boolean contains(IRType type) { |
| return types.contains(type); |
| } |
| |
| @Override |
| public boolean containsAll(JSTypeSet types) { |
| for (IRType type : types) { |
| if (!contains(type)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| @Override |
| public String toString() { |
| return types.toString(); |
| } |
| |
| } |
| |
| public static JSTypeSet create() { |
| return new JSTypeSetImpl(); |
| } |
| |
| public static JSTypeSet create(IRType type) { |
| if (type == null || type == RTypes.none()) { |
| return emptySet(); |
| } else { |
| final JSTypeSet set = new JSTypeSetImpl(); |
| set.add(type); |
| return set; |
| } |
| } |
| |
| public static JSTypeSet of(Iterable<? extends IRType> argTypes) { |
| if (argTypes instanceof JSTypeSet) { |
| return (JSTypeSet) argTypes; |
| } else { |
| final Iterator<? extends IRType> i = argTypes.iterator(); |
| if (i.hasNext()) { |
| final JSTypeSet result = create(); |
| do { |
| result.add(i.next()); |
| } while (i.hasNext()); |
| return result; |
| } else { |
| return emptySet(); |
| } |
| } |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (obj instanceof JSTypeSet) { |
| final JSTypeSet other = (JSTypeSet) obj; |
| return size() == other.size() && containsAll(other); |
| } |
| return false; |
| } |
| |
| public abstract void add(IRType type); |
| |
| @Deprecated |
| public abstract IRType getFirst(); |
| |
| /** |
| * Returns this type set as {@link IRType}. If type set is empty then |
| * <code>null</code> is returned, if type set contains single entry then |
| * that entry is returned, otherwise union type is created and returned. |
| */ |
| public abstract IRType toRType(); |
| |
| @Deprecated |
| public abstract Type[] toArray(); |
| |
| public abstract int size(); |
| |
| public abstract void addAll(Iterable<IRType> types); |
| |
| public abstract boolean isEmpty(); |
| |
| public abstract void clear(); |
| |
| public abstract boolean contains(IRType type); |
| |
| public abstract boolean containsAll(JSTypeSet types); |
| |
| } |