/******************************************************************************* * This file is part of OpenNMS(R). * * Copyright (C) 2011 The OpenNMS Group, Inc. * OpenNMS(R) is Copyright (C) 1999-2011 The OpenNMS Group, Inc. * * OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc. * * OpenNMS(R) is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation, either version 3 of the License, * or (at your option) any later version. * * OpenNMS(R) is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenNMS(R). If not, see: * http://www.gnu.org/licenses/ * * For more information contact: * OpenNMS(R) Licensing <license@opennms.org> * http://www.opennms.org/ * http://www.opennms.com/ *******************************************************************************/ package org.opennms.netmgt.config; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.Iterator; import java.util.List; // TODO make this implement Collection /** * <p>TimeIntervalSequence class.</p> * * @author ranger * @version $Id: $ */ public abstract class AbstractTimeIntervalSequence<T extends TimeInterval> { private static class TimeIntervalSeqIter<T extends TimeInterval> implements Iterator<T> { private AbstractTimeIntervalSequence<T> m_current; public TimeIntervalSeqIter(AbstractTimeIntervalSequence<T> seq) { m_current = seq; } public boolean hasNext() { return m_current != null && m_current.m_interval != null; } public T next() { T interval = m_current.m_interval; m_current = m_current.m_tail; return interval; } public void remove() { throw new UnsupportedOperationException("not implemented yet"); } } private T m_interval; private AbstractTimeIntervalSequence<T> m_tail; /** * <p>Constructor for TimeIntervalSequence.</p> */ public AbstractTimeIntervalSequence() { this(null, null); } /** * <p>Constructor for TimeIntervalSequence.</p> * * @param interval a {@link org.opennms.netmgt.config.TimeInterval} object. */ public AbstractTimeIntervalSequence(T interval) { this(interval, null); } private AbstractTimeIntervalSequence(T interval, AbstractTimeIntervalSequence<T> tail) { m_interval = interval; m_tail = tail; } /** * <p>iterator</p> * * @return a {@link java.util.Iterator} object. */ public Iterator<T> iterator() { return new TimeIntervalSeqIter<T>(this); } Date min(Date a, Date b) { return (a.before(b) ? a : b); } Date max(Date a, Date b) { return (b.before(a) ? a: b); } /** * <p>addInterval</p> * * @param interval a {@link org.opennms.netmgt.config.TimeInterval} object. */ public void addInterval(T interval) { if (m_interval == null) { m_interval = interval; } else if (m_interval.preceeds(interval)) { addPreceedingInterval(interval); } else if (m_interval.follows(interval)) { addSucceedingInterval(interval); } else if (m_interval.overlaps(interval)) { addOverlappingInterval(interval); } } private void addOverlappingInterval(T newInterval) { // overlapping intervals Collection<T> newIntervals = combineIntervals(m_interval, newInterval); // remove the current interval since we are replacing it with the new ones removeCurrent(); // now add the new intervals addAll(newIntervals); } /** * <p>combineIntervals</p> * * @param currentInterval a {@link org.opennms.netmgt.config.TimeInterval} object. * @param newInterval a {@link org.opennms.netmgt.config.TimeInterval} object. * @return a {@link java.util.Collection} object. */ protected Collection<T> combineIntervals(T currentInterval, T newInterval) { List<T> newIntervals = new ArrayList<T>(3); // overlapping intervals get divided into three non-overlapping segments // that are bounded by the below Date first = min(currentInterval.getStart(), newInterval.getStart()); Date second = max(currentInterval.getStart(), newInterval.getStart()); Date third = min(currentInterval.getEnd(), newInterval.getEnd()); Date fourth = max(currentInterval.getEnd(), newInterval.getEnd()); // Construct up to three non-overlapping intervals that can be added to the list if (first.equals(second)) { // if the first segment is empty then the second segment because head of the list // the second segment is not empty because intervals can't be empty newIntervals.add(createInterval(first, third)); } else { // first segment is not empty make it head of the list and add the // second the the tail newIntervals.add(createInterval(first, second)); newIntervals.add(createInterval(second, third)); } if (!third.equals(fourth)) { // if the third segment no empty add it to the tail as well. // Note: this segment may overlap with the original lists next interval newIntervals.add(createInterval(third, fourth)); } return newIntervals; } private void addSucceedingInterval(T interval) { // new interval is earlier than current interval // replace current with new and add current to the tail AbstractTimeIntervalSequence<T> oldTail = m_tail; m_tail = createTail(m_interval); m_tail.m_tail = oldTail; m_interval = interval; } private void addPreceedingInterval(T interval) { // new interval is later than current interval so add it to the tail addToTail(interval); } private void addToTail(T interval) { if (m_tail == null) { m_tail = createTail(interval); } else { m_tail.addInterval(interval); } } /** * <p>createInterval</p> * * @param start a {@link java.util.Date} object. * @param end a {@link java.util.Date} object. * @return a {@link org.opennms.netmgt.config.TimeInterval} object. */ protected abstract T createInterval(Date start, Date end); /** * <p>createTail</p> * * @param interval a {@link org.opennms.netmgt.config.TimeInterval} object. * @return a {@link org.opennms.netmgt.config.AbstractTimeIntervalSequence} object. */ protected abstract AbstractTimeIntervalSequence<T> createTail(T interval); private void removeCurrent() { if (m_tail == null) { m_interval = null; } else { m_interval = m_tail.m_interval; m_tail = m_tail.m_tail; } } /** * <p>removeInterval</p> * * @param removedInterval a {@link org.opennms.netmgt.config.TimeInterval} object. */ public void removeInterval(T removedInterval) { if (m_interval == null) { return; } if (m_interval.preceeds(removedInterval)) { removeFromTail(removedInterval); } else if (m_interval.follows(removedInterval)) { // no need to do anything because the entire remove interval is before this return; } else if (m_interval.overlaps(removedInterval)) { T origInterval = m_interval; // remove the region from the tail of sequence removeFromTail(removedInterval); // remove the current element removeCurrent(); // add back any part of the original interval that follows the remove interval Collection<T> newIntervals = separateIntervals(origInterval, removedInterval); addAll(newIntervals); } } /** * <p>separateIntervals</p> * * @param origInterval a {@link org.opennms.netmgt.config.TimeInterval} object. * @param removedInterval a {@link org.opennms.netmgt.config.TimeInterval} object. * @return a {@link java.util.Collection} object. */ protected Collection<T> separateIntervals(T origInterval, T removedInterval) { List<T> newIntervals = new ArrayList<T>(2); if (removedInterval.getEnd().before(origInterval.getEnd())) { newIntervals.add(createInterval(removedInterval.getEnd(), origInterval.getEnd())); } // add back any part of the original interval the preceeded the remove interval if (origInterval.getStart().before(removedInterval.getStart())) { newIntervals.add(createInterval(origInterval.getStart(), removedInterval.getStart())); } return newIntervals; } private void removeFromTail(T interval) { if (m_tail == null) { return; } m_tail.removeInterval(interval); if (m_tail.m_interval == null) { m_tail = null; } } /** * <p>bound</p> * * @param start a {@link java.util.Date} object. * @param end a {@link java.util.Date} object. */ public void bound(Date start, Date end) { removeInterval(createInterval(new Date(0), start)); removeInterval(createInterval(end, new Date(Long.MAX_VALUE))); } /** * <p>bound</p> * * @param interval a {@link org.opennms.netmgt.config.TimeInterval} object. */ public void bound(T interval) { bound(interval.getStart(), interval.getEnd()); } /** * <p>getStart</p> * * @return a {@link java.util.Date} object. */ public Date getStart() { if (m_interval == null) return null; return m_interval.getStart(); } /** * <p>getEnd</p> * * @return a {@link java.util.Date} object. */ public Date getEnd() { if (m_interval == null) return null; if (m_tail == null) return m_interval.getEnd(); return m_tail.getEnd(); } /** * <p>getBounds</p> * * @return a {@link org.opennms.netmgt.config.TimeInterval} object. */ public TimeInterval getBounds() { Date start = getStart(); Date end = getEnd(); return (start == null || end == null ? null : new TimeInterval(start, end)); } /** * <p>addAll</p> * * @param intervals a {@link org.opennms.netmgt.config.AbstractTimeIntervalSequence} object. */ public void addAll(AbstractTimeIntervalSequence<T> intervals) { for (Iterator<T> it = intervals.iterator(); it.hasNext();) { T interval = it.next(); addInterval(interval); } } /** * <p>addAll</p> * * @param intervals a {@link java.util.Collection} object. */ public void addAll(Collection<T> intervals) { for (Iterator<T> it = intervals.iterator(); it.hasNext();) { T interval = it.next(); addInterval(interval); } } /** * <p>removeAll</p> * * @param intervals a {@link org.opennms.netmgt.config.AbstractTimeIntervalSequence} object. */ public void removeAll(AbstractTimeIntervalSequence<T> intervals) { for (Iterator<T> it = intervals.iterator(); it.hasNext();) { T interval = it.next(); removeInterval(interval); } } /** * <p>toString</p> * * @return a {@link java.lang.String} object. */ public String toString() { StringBuffer buf = new StringBuffer("["); boolean first = true; for (Iterator<? extends TimeInterval> it = this.iterator(); it.hasNext();) { TimeInterval interval = (TimeInterval) it.next(); if (first) { first = false; } else { buf.append(","); } buf.append(interval); } buf.append(']'); return buf.toString(); } }