/* * Copyright 2014 Google Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.samples.apps.iosched.model; import java.util.*; public class ScheduleItemHelper { private static final long FREE_BLOCK_MINIMUM_LENGTH = 10 * 60 * 1000; // 10 minutes public static final long ALLOWED_OVERLAP = 5 * 60 * 1000; // 5 minutes /** * Find and resolve time slot conflicts. * Items should already be ordered by start time. Conflicts among mutableItems, if any, * won't be checked, and they will be left as is. **/ static public ArrayList<ScheduleItem> processItems(ArrayList<ScheduleItem> mutableItems, ArrayList<ScheduleItem> immutableItems) { // move mutables as necessary to accommodate conflicts with immutables: moveMutables(mutableItems, immutableItems); // mark conflicting immutable: markConflicting(immutableItems); ArrayList<ScheduleItem> result = new ArrayList<ScheduleItem>(); result.addAll(immutableItems); result.addAll(mutableItems); Collections.sort(result, new Comparator<ScheduleItem>() { @Override public int compare(ScheduleItem lhs, ScheduleItem rhs) { return lhs.startTime < rhs.startTime ? -1 : 1; } }); return result; } static protected void markConflicting(ArrayList<ScheduleItem> items) { for (int i=0; i<items.size(); i++) { ScheduleItem item = items.get(i); // Notice that we only care about sessions when checking conflicts. if (item.type == ScheduleItem.SESSION) for (int j=i+1; j<items.size(); j++) { ScheduleItem other = items.get(j); if (item.type == ScheduleItem.SESSION) { if (intersect(other, item, true)) { other.flags |= ScheduleItem.FLAG_CONFLICTS_WITH_PREVIOUS; } else { // we assume the list is ordered by starttime break; } } } } } static protected void moveMutables(ArrayList<ScheduleItem> mutableItems, ArrayList<ScheduleItem> immutableItems) { Iterator<ScheduleItem> immutableIt = immutableItems.iterator(); while (immutableIt.hasNext()) { ScheduleItem immutableItem = immutableIt.next(); if (immutableItem.type == ScheduleItem.BREAK) { // Breaks (lunch, after hours, etc) should not make free blocks to move continue; } ListIterator<ScheduleItem> mutableIt = mutableItems.listIterator(); while (mutableIt.hasNext()) { ScheduleItem mutableItem = mutableIt.next(); ScheduleItem split = null; // If mutable item is overlapping the immutable one if (intersect(immutableItem, mutableItem, true)) { if (isContainedInto(mutableItem, immutableItem)) { // if mutable is entirely contained into immutable, just remove it mutableIt.remove(); continue; } else if (isContainedInto(immutableItem, mutableItem)) { // if immutable is entirely contained into mutable, split mutable if necessary: if (isIntervalLongEnough(immutableItem.endTime, mutableItem.endTime)) { split = (ScheduleItem) mutableItem.clone(); split.startTime = immutableItem.endTime; } mutableItem.endTime = immutableItem.startTime; } else if (mutableItem.startTime < immutableItem.endTime) { // Adjust the start of the mutable mutableItem.startTime = immutableItem.endTime; } else if (mutableItem.endTime > immutableItem.startTime) { // Adjust the end of the mutable mutableItem.endTime = immutableItem.startTime; } if (!isIntervalLongEnough(mutableItem.startTime, mutableItem.endTime)) { mutableIt.remove(); } if (split != null) { mutableIt.add(split); } } } } } static private boolean isIntervalLongEnough(long start, long end) { return ( end - start ) >= FREE_BLOCK_MINIMUM_LENGTH; } static private boolean intersect(ScheduleItem block1, ScheduleItem block2, boolean useOverlap) { return block2.endTime > ( block1.startTime + ( useOverlap ? ALLOWED_OVERLAP : 0 ) ) && ( block2.startTime + ( useOverlap ?ALLOWED_OVERLAP : 0 ) ) < block1.endTime; } static private boolean isContainedInto(ScheduleItem contained, ScheduleItem container) { return contained.startTime >= container.startTime && contained.endTime <= container.endTime; } }