package com.laytonsmith.core.constructs; import com.laytonsmith.PureUtilities.Version; import com.laytonsmith.annotations.typeof; import com.laytonsmith.core.CHVersion; import com.laytonsmith.core.Static; import com.laytonsmith.core.exceptions.CRE.CRECastException; import com.laytonsmith.core.exceptions.CRE.CRERangeException; import com.laytonsmith.core.exceptions.ConfigCompileException; import com.laytonsmith.core.exceptions.ConfigRuntimeException; import com.laytonsmith.core.functions.ArrayHandling; import java.util.AbstractSet; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.Stack; /** * * */ @typeof("slice") public class CSlice extends CArray { private long start; private long finish; private int direction; private long max; private long size; public CSlice(String slice, Target t) throws ConfigCompileException{ super(t); String [] split = slice.split("\\.\\."); if(split.length > 2){ throw new ConfigCompileException("Invalid slice notation! (" + slice + ")", t); } String sstart; String sfinish; if(split.length == 1){ sstart = slice.trim().substring(0, slice.trim().length() - 2); sfinish = "-1"; } else { if(slice.trim().startsWith("..")){ sstart = "0"; sfinish = slice.trim().substring(2); } else { sstart = split[0]; sfinish = split[1]; } } try{ start = Long.parseLong(sstart.trim()); finish = Long.parseLong(sfinish.trim()); } catch(NumberFormatException e){ throw new CRECastException("Expecting integer in a slice, but was given \"" + sstart + "\" and \"" + sfinish + "\"", t); } calculateCaches(); } public CSlice(long from, long to, Target t){ super(t); this.start = from; this.finish = to; calculateCaches(); } @Override public List<Construct> asList() { CArray ca = new ArrayHandling.range().exec(Target.UNKNOWN, null, new CInt(start, Target.UNKNOWN), new CInt(finish, Target.UNKNOWN)); return ca.asList(); } private void calculateCaches(){ direction = start < finish?1:start==finish?0:-1; max = Math.abs(finish - start); size = max + 1; } public long getStart(){ return start; } public long getFinish(){ return finish; } @Override public boolean isDynamic() { return false; } @Override public String val() { return start + ".." + finish; } @Override public String toString() { return val(); } @Override protected String getString(Stack<CArray> arrays, Target t) { //We don't need to consider arrays, because we can't //get stuck in an infinite loop. return val(); } @Override public boolean inAssociativeMode() { return false; } @Override public void set(Construct index, Construct c, Target t) { throw new CRECastException("CSlices cannot set values", t); } @Override public Construct get(Construct index, Target t) { long i = Static.getInt(index, t); if(i > max){ throw new CRERangeException("Index out of bounds. Index: " + i + " Size: " + max, t); } return new CInt(start + (direction * i), t); } @Override public Set<Construct> keySet() { // To keep our memory footprint down, we create a "fake" keyset here, which doesn't // require actually creating an entire Set. Removing items from the set isn't supported, // but all iteration options are. return new AbstractSet<Construct>() { @Override public Iterator<Construct> iterator() { return new Iterator<Construct>() { int index = 0; @Override public boolean hasNext() { return index < size; } @Override public Construct next() { return new CInt(index++, Target.UNKNOWN); } @Override public void remove() { throw new UnsupportedOperationException("Not supported yet."); } }; } @Override public int size() { return (int) CSlice.this.size(); } }; } @Override public long size() { return size; } @Override public boolean contains(Construct c) { try{ long i = Static.getInt(c, Target.UNKNOWN); if(start < finish){ return start <= i && i <= finish; } else { return start >= i && i <= finish; } } catch(ConfigRuntimeException e){ return false; } } @Override public boolean containsKey(String c) { try{ long i = Long.parseLong(c); return i >= 0 && i < size; } catch(NumberFormatException e){ return false; } } @Override public String docs() { return "A slice is a value that represents a numeric range, either in the positive direction, or negative."; } @Override public Version since() { return CHVersion.V3_3_1; } }