blob: e53937b2fe4a1f5df5897f9f51e1f50cd1584d65 [file] [log] [blame]
/*******************************************************************************
* 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);
}