SimpleTimeLine.java

package eu.javaexperience.time;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;

public class SimpleTimeLine<T extends TimeLineElement<T>> implements Iterable<T>
{
	private T begin;
	private T end;

	/**
	 * Hogy olyan STL-t adsz-e amin tényleg szerepe-e az az elem, az csak a te becsületeden múlik.
	 * Érdemes, különben az eredmény lappangó hibát okozhat.
	 * */
	public static <T extends TimeLineElement<T>> boolean unifyWithNextWithNextOverride(SimpleTimeLine<T> stl,T elem)
	{
		T next = elem.next;
		if(next != null)
		{
			stl.delete(next);
			elem.to = next.to;
			elem.dTo = next.dTo;
			return true;
		}
		
		return false;
	}
	
	public int unifyElements(TimeLineElementUnifier<T> uni)
	{
		if(begin == null)
			return 0;
		
		int num = 0;
		T el = begin;
		while(el != null)
		{
			if(el.next != null)
				if(uni.mayUnify(el, el.next))
				{
					el.to = el.next.to;
					el.dTo = el.next.dTo;
					internal_delete(el.next, true);
					++num;
					continue;
				}
			
			el = el.next;
		}
		return num;
	}
	
	public int mergeElements(TimeLineElementMerger<T> merger)
	{
		if(begin == null)
			return 0;
		
		int num = 0;
		T el = begin;
		while(el != null)
		{
			if(el.next != null)
			{
				T merge = merger.merge(el, el.next);
				if(merge != null)
				{
					/*
					 * ha nincs előző elem az azt jelenti hogy ez ez idővonal első eleme
					 * az összeolvaszott elem az első elem lesz
					 */
					if(el.prev == null)
					{
						//nincs előző elem
						merge.prev = null;
						//idővonal kezdő eleme ez
						begin = merge;
					}
					else
					{
						//különben az előző elem a felülírandó előző eleme
						merge.prev = el.prev;
					}
					
					/*
					 * ha az összeolvaszott után nincs következő elem akkor akkor ez az
					 * utolsó elem az idővonalon.
					 */
					if(el.next.next == null)
					{
						end = merge;
						merge.next = null;
					}
					else
					{
						merge.next = el.next.next;
					}
					
					el = merge.next;
					
					++num;
					//lehet hogy ezt a blokkot az ez után következővel még öszze lehet vonni
					continue;
				}
			}
			el = el.next;
		}

		return num;
	}
	
	
	public int splitElement(TimeLineElementSplitter<T> splitter,T elem)
	{
		if(!contains(elem))
			return 0;
		
		int nums = 0;
		
		T[] elems = splitter.split(elem);
		internal_delete(elem, true);
		for(T e:elems)
		{
			add(e);
			nums++;
		}
		return nums;
	}
	
	public int splitAllElement(TimeLineElementSplitter<T> splitter)
	{
		int num = 0;
		for(T e:this)
		{
			T[] elems = splitter.split(e);
			if(elems.length == 1)
				if(elems[0] == e)
					continue;
			
			T next = e.next;
			T prev = e.prev;
			internal_delete(e, true);
			for(T i:elems)
			{
				fastAddBTW(prev, i, next);
				prev = i;
				//add(i);
				num++;
			}
		}

		return num;
	}
	
	public boolean delete(T elem)
	{
		return internal_delete(elem, false);
	}
	
	private boolean internal_delete(T elem,boolean sureContains)
	{
		//elsőt, utolsót, közbenlévőt
		if(begin != null)//ha begin null akkor az end is null!
		{
			if(elem == begin && begin == end)//csak ez az elem van benn.
			{
				unlinkElement(elem);
				begin = end = null;
				ep = 0;
				return true;
			}
			
			if(begin == elem)//kezdő elem törtlése
			{
				//kell lennie köv elemnek
				if(begin.next.next != null)
					linkElement(null, begin.next, begin.next.next);//van még 2 elem
				else
					linkElement(null, begin.next, null);//törlöm és már csak 1 vam
				unlinkElement(elem);
				ep--;
				return true;
			}

			if(elem == end)// záró elem törlése
			{
				//kell lennie előző elemnek
				if(end.prev.prev != null)
					linkElement(end.prev.prev, end.prev, null);//van még 2 elem
				else
					linkElement(null, end.prev, null);//törlöm és már csak 1 vam
				unlinkElement(elem);
				ep--;
				return true;
			}
		}
		
		//köztes elem törlése
		//csak az a kérdés hogy benne-van-e
		if(sureContains || contains(elem))
		{
			//kereszthivatkozás
			elem.prev.next = elem.next;
			elem.next.prev = elem.prev;
			unlinkElement(elem);
			ep--;
			return true;
		}
		
		return false;
	}
	
	public String getInconsistenty()
	{
		if(begin == end)
			return "minden oké";
		
		StringBuilder sb = new StringBuilder();
		
		ArrayList<T> elems = new ArrayList<>();
		
		for(T i = begin;i != end;i=i.getNext())
		{
			if(i!= null)
			{
				if(elems.contains(i))
					sb.append("Dupla elem: "+i+"\n");
				else
					elems.add(i);
			}	
			if(i.getNext() == null)
				sb.append("null menet közben\n");
		}
		return sb.toString();
	}
	
	public boolean contains(T elem)
	{
		if(begin == null)
			return false;
		
		for(T e = begin;e.next != null;e = e.next)
			if(e == elem)
				return true;
		
		return false;
	}

	public int cutAllBeforeElement(T elem)
	{
		if(begin == null)
			return 0;

		int num = 1;
		for(T e = begin;e.next != null;e = e.next, ++num)
			if(e == elem)
			{
				linkElement(null, e, e.next);
				break;
			}
		
		return num;
	}
	
	public int cutAllAfterElement(T elem)
	{
		if(end == null)
			return 0;

		int num = 1;
		for(T e = end;e.prev != null;e = e.prev, ++num)
			if(e == elem)
			{
				linkElement(e.prev, e, null);
				break;
			}
		
		return num;
	}

	private boolean fastAddBTW(T prev,T disz,T next)
	{
		//ha az after this null akkor a végére a sima add-al vagy az afterThis.next null
		if(prev == null || next == null)
			return add(disz);
		
		
		
		if(disz.isBTW(prev.to, next.from))//ha befér linkeljük
		{
			linkElement(prev, disz, next);
			++ep;
			return true;
		}

		return false;
	}
	
	public boolean add(T elem)
	{
//		if(elem == null) //felesleges
//			throw new IllegalArgumentException("Az elem nem lehet null!");
		
		
//try{
		if(begin == null) //első elem hozzáadása
		{
			linkElement(null, elem, null);
			return true;
		}
		
		if(elem.isBefore(begin))
		{
			linkElement(null, elem, begin);
			return true;
		}
		
		if(elem.isAfter(end))
		{
			linkElement(end, elem, null);
			return true;
		}

		if(elem.hasCommonSectionWith(begin))
			return false;
		
		for(T el = begin;el != null;el = el.next)
		{
			if(elem.hasCommonSectionWith(el))
				return false;
			
			if(el.next != null)
			{
				if(elem.isBTW(el.to, el.next.from))
				{
					linkElement(el, elem,  el.next);
					return true;
				}
			}
			else
				return false;//az előbb már vizsgáltuk (nem az utolsó után van)
		}
//}finally{if(this.size()>400_000){try {DDSI.writeStringToFile(toString(),"/tmp/stl");} catch (IOException e) {e.printStackTrace();}System.exit(0);}}
		
		System.out.println(toString());
		throw new IllegalStateException("Rossz implementáció! na ekkor mi van?");
	}
	
	private int ep = 0;
	
	private void unlinkElement(T elem)
	{
		elem.next = null;
		elem.prev = null;
	}
	
	/**
	 * Megadott elemek közé beszúrja az adott időelem blokkot.
	 * 
	 * (null,elem,null)	: első beszúrás, kezdő és vég elem elem lesz.
	 * (null,elem,first): az idővonal elejére teszi.
	 * (last,elem,null) : idővopnal végére teszi
	 * (t0,elem,t1)		: t0 és t1 közé teszi, előzőleg vizsgálni kell hogy meg lehet-e tenni, ezért privát ez a metódus
	 * */
	private void linkElement(T prev,T midd,T next)
	{
		if(prev == null && next == null)// a legeslegelső elem
		{
			midd.prev = null;
			midd.next = null;
			begin = midd;
			end = midd;
		}
		else if(prev == null)//első elemként linkelés
		{
		//	begin.prev = null;
		//	begin.next = null;
			midd.next = next;
			next.prev = midd;
			midd.prev = null;
			begin = midd;
		}
		else if(next == null)//utolsó elemként linkelés
		{
			/*if(next == end)
			{
				end.next = null;
				end.prev = null;
			}*/
			
			prev.next = midd;
			midd.prev = prev;
			midd.next = null;
			end = midd;
		}
		else
		{
			prev.next = midd;
			midd.next = next;

			next.prev = midd;
			midd.prev = prev;
		}
		ep++;
	}
	
	public T getFirst()
	{
		return begin;
	}
	
	public T getLast()
	{
		return end;
	}
	
	public String toString()
	{
		StringBuilder sb = new StringBuilder();
		sb.append("SimpleTimeLine\n{\n\t");
		TimeLineElement<T> el = begin;
		if(el != null)
		{
			sb.append(el.toString());
			
			while((el = el.next) != null)
			{
				sb.append("\n\t");
				sb.append(el.toString());
			}
		}

		sb.append("\n}\n");
		return sb.toString();
	}
	
	public Date getFullRangeStart()
	{
		if(begin == null)
			return null;
		return begin.getDateFrom();
	}
	
	public Date getFullRangeEnd()
	{
		if(end == null)
			return null;
		return end.getDateTo();
	}

	public T getElementAt(Date t)
	{
		T elem = begin;
		long et = t.getTime();
		while(elem != null)
		{
			if(elem.isIn(et))
				return elem;
			
			elem =  elem.next;
		}

		return null;
	}
	
	public T getFirstElementAfter(Date t)
	{
		T elem = begin;
		long et = t.getTime();
		while(elem != null)
		{
			if(elem.isAfter(et))
				return elem;
			
			elem =  elem.next;
		}

		return null;
	}
	
	public T getFirstElementBetween(Date from, Date to)
	{
		T ret = getFirstElementAfter(from);
		if(null == ret)
		{
			return null;
		}
		
		if(ret.isBTW(from, to))
		{
			return ret;
		}
		
		return null;
	}
	
	public T getFirstElementBetweenIntersects(Date from, Date to)
	{
		T elem = begin;
		while(elem != null)
		{
			if(elem.isIntersects(from, to))
				return elem;
			
			elem =  elem.next;
		}

		return null;
	}
	
	public void getElementsDurationIs(long inMilisec,Collection<T> store)
	{
		T elem = begin;
		while(elem != null)
		{
			if(elem.duration() == inMilisec)
				store.add(elem);
			
			elem = elem.next;
		}
	}
	
	public void getElementsDurationIsNot(long inMilisec,Collection<T> store)
	{
		T elem = begin;
		while(elem != null)
		{
			if(elem.duration() != inMilisec)
				store.add(elem);
			
			elem = elem.next;
		}
	}
	
	public void getElementsDurationGT(long inMilisec,Collection<T> store)
	{
		T elem = begin;
		while(elem != null)
		{
			if(elem.duration() > inMilisec)
				store.add(elem);
			
			elem = elem.next;
		}
	}
	
	public void getElementsDurationGTE(long inMilisec,Collection<T> store)
	{
		T elem = begin;
		while(elem != null)
		{
			if(elem.duration() >= inMilisec)
				store.add(elem);
			
			elem = elem.next;
		}
	}
	
	public void getElementsDurationLT(long inMilisec,Collection<T> store)
	{
		T elem = begin;
		while(elem != null)
		{
			if(elem.duration() < inMilisec)
				store.add(elem);
			
			elem = elem.next;
		}
	}
	
	public void getElementsDurationLTE(long inMilisec,Collection<T> store)
	{
		T elem = begin;
		while(elem != null)
		{
			if(elem.duration() <= inMilisec)
				store.add(elem);
			
			elem = elem.next;
		}
	}
	
	public void getElementsAfter(Date date,Collection<T> store)
	{
		T elem = begin;
		long t0 = date.getTime();
		while(elem != null)
		{
			if(elem.isAfter(t0))
				while(elem != null)
				{
					store.add(elem);
					elem = elem.next;
				}
			else
				elem = elem.next;
		}
	}
	
	public int size()
	{	
		return ep;
	}
	
	public T get(int n)
	{
		T ret = getFirst();
		while(null != ret && --n >= 0)
		{
			ret = ret.next;
		}
		return ret;
	}
	
	@Override
	public Iterator<T> iterator() {
		return new Iterator<T>()
		{
			private T elem = begin;
			@Override
			public boolean hasNext()
			{
				return elem != null;
			}

			@Override
			public T next()
			{
				T ret = elem;
				if(elem != null)
					elem = elem.next;
				else
					elem = null;
				return ret;
			}

			@Override
			public void remove() {
				throw new IllegalStateException("nincs megvalósítva");
			}
		};
	}
}