/*
* Copyright 2010 The Rabbit Eclipse Plug-in Project
*
* 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 rabbit.data.internal.xml.store;
import static rabbit.data.internal.xml.DatatypeUtil.isSameDate;
import static rabbit.data.internal.xml.DatatypeUtil.isSameMonthInYear;
import static rabbit.data.internal.xml.DatatypeUtil.toXmlDate;
import rabbit.data.internal.xml.IDataStore;
import rabbit.data.internal.xml.XmlPlugin;
import rabbit.data.internal.xml.convert.IConverter;
import rabbit.data.internal.xml.merge.IMerger;
import rabbit.data.internal.xml.merge.Mergers;
import rabbit.data.internal.xml.schema.events.EventGroupType;
import rabbit.data.internal.xml.schema.events.EventListType;
import rabbit.data.internal.xml.schema.events.ObjectFactory;
import rabbit.data.store.IStorer;
import rabbit.data.store.model.DiscreteEvent;
import static com.google.common.base.Preconditions.checkNotNull;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import java.io.File;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import javax.xml.datatype.XMLGregorianCalendar;
/**
* This abstract class is designed specifically for the XML schema. This class
* contains implementations for common behaviors.
*
* @param <E> The event type.
* @param <T> The corresponding XML object type of the event type, this is the
* form when the event is stored in XML.
* @param <S> A category type that holds the XML types according to event date.
*/
public abstract class AbstractStorer<E extends DiscreteEvent, T, S extends EventGroupType>
implements IStorer<E> {
/** Factory object for creating XML schema Java types. */
protected final ObjectFactory objectFactory;
/** Data in memory, not yet saved. */
private final Set<S> data;
/** The current month. */
private LocalDate currentMonth;
/** Converter for converting an event to its corresponding XML type. */
private final IConverter<E, T> converter;
/** Merger for merging two XML types. */
private final IMerger<T> merger;
/** The data store to store the data to. */
private final IDataStore store;
/**
* Constructor.
*
* @param converter Converter for converting an event to its corresponding XML
* type.
* @param merger Merger for merging two XML types.
* @param store The data store to store the data to.
*/
protected AbstractStorer(IConverter<E, T> converter, IMerger<T> merger,
IDataStore store) {
this.converter = checkNotNull(converter);
this.merger = checkNotNull(merger);
this.store = checkNotNull(store);
data = new LinkedHashSet<S>();
currentMonth = new LocalDate();
objectFactory = new ObjectFactory();
}
@Override
public void commit() {
if (data.isEmpty())
return;
File f = getDataStore().getDataFile(currentMonth);
EventListType events = getDataStore().read(f);
List<S> categories = getCategories(events);
IMerger<T> merger = getMerger();
for (S mergeFrom : data) {
boolean done = false;
for (S mergeTo : categories) {
if (mergeFrom.getDate().equals(mergeTo.getDate())) {
if (merger != null)
Mergers.merge(merger, getElements(mergeTo), getElements(mergeFrom));
else
getElements(mergeTo).addAll(getElements(mergeFrom));
done = true;
break;
}
}
if (!done) {
categories.add(mergeFrom);
}
}
if (!getDataStore().write(events, f)) {
XmlPlugin.getDefault().getLog().log(
new Status(IStatus.ERROR, XmlPlugin.PLUGIN_ID, "Unable to save data."));
}
data.clear();
}
@Override
public void insert(Collection<? extends E> collection) {
for (E elements : collection)
insert(elements);
}
@Override
public void insert(E event) {
DateTime time = event.getTime();
if (!isSameMonthInYear(event.getTime(), currentMonth)) {
commit();
currentMonth = time.toLocalDate();
}
IMerger<T> merger = getMerger();
T element = getConverter().convert(event);
for (S category : data) {
if (isSameDate(event.getTime(), category.getDate())) {
if (merger != null)
Mergers.merge(merger, getElements(category), element);
else
getElements(category).add(element);
return;
}
}
S category = newCategory(toXmlDate(event.getTime()));
getElements(category).add(element);
data.add(category);
}
/**
* Gets the XML categories for grouping the event objects by date in a
* {@link EventListType}.
*
* @param events The root element.
* @return A list of groups.
*/
protected abstract List<S> getCategories(EventListType events);
/**
* Gets a converter for converting events to XML types.
*
* @return A converter, must not be null.
*/
protected final IConverter<E, T> getConverter() {
return converter;
}
/**
* Gets the data store.
*
* @return The data store.
*/
protected final IDataStore getDataStore() {
return store;
}
/**
* Gets the XML elements from the given group.
*
* @param list The group holding the XML types.
* @return A list of XML elements.
*/
protected abstract List<T> getElements(S list);
/**
* Gets the merger for merging identical elements.
*
* @return An {@linkplain IMerger}, or null if all elements are to be treated
* uniquely.
*/
protected final IMerger<T> getMerger() {
return merger;
}
/**
* Creates a new category from the given date.
*
* @param date The date.
* @return A new category configured with the date.
*/
protected abstract S newCategory(XMLGregorianCalendar date);
}