blob: c042120e63baeb5f905e59b448e56ded8c811fc0 [file] [log] [blame]
// Record.java
package org.eclipse.stem.internal.data.specifications;
/*******************************************************************************
* Copyright (c) 2009 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
import java.io.File;
import java.io.IOException;
import java.text.DateFormat;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Properties;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.URIConverter;
import org.eclipse.emf.ecore.resource.impl.URIConverterImpl;
import org.eclipse.stem.core.Utility;
import org.eclipse.stem.core.common.CommonFactory;
import org.eclipse.stem.core.common.DublinCore;
import org.eclipse.stem.core.common.Identifiable;
/**
* This class represents the specification of an
* {@link org.eclipse.stem.core.common.Identifiable} that can be used to
* create an instance of one. The specification could come from a property file,
* or could be generated programmatically.
*/
@SuppressWarnings("deprecation")
abstract public class IdentifiableSpecification {
/**
* This is a static instance used when serializing instances.
*/
protected static final URIConverter converter = new URIConverterImpl();
/**
* This is a static instance used when formatting the start and end dates.
*
* @see #createValidDateRangeString(DateFormat, String)
* @see #SERIALIZATION_DATE_RANGE_FORMAT_2_DATES
*/
protected static final DateFormat SERIALIZATION_CONTENT_DESCRIPTOR_DATE_FORMATER = new SimpleDateFormat(
"yyyyMMdd");
/**
* This is the format used to create date range strings used in file and
* folder names.
*
*/
public static final String SERIALIZATION_DATE_RANGE_FORMAT_2_DATES = "{0}{1}";
/**
* This is the format used to create date range strings used in file and
* folder names.
*
*/
public static final String SERIALIZATION_DATE_RANGE_FORMAT_1_DATE = "{0}";
/**
* This is the format used to create date range strings used in file and
* folder names.
*
*/
public static final String SERIALIZATION_DATE_RANGE_FORMAT_1_DATE_YEAR = "{0}";
/**
* The {@link Identifiable} that this instance specifies. This is created on
* demand when there is a call to {@link #getIdentifiable()}
*
* @see #createIdentifiable()
*/
protected Identifiable identifiable = null;
/**
* The {@link DublinCore} metadata for the {@link Identifiable}. The values
* for this are collected from the data properties files before the
* {@link Identifiable} is created.
*
* @see #collectNonDataProperties(Properties)
*/
protected DublinCore dublinCore = null;
/**
* Default constructor
*/
protected IdentifiableSpecification() {
dublinCore = CommonFactory.eINSTANCE.createDublinCore();
dublinCore.populate();
} // IdentifiableSpecification
/**
* @param startDate
* the starting date that the IdentifiableSpecification's
* contents are valid
*/
protected IdentifiableSpecification(final Date startDate) {
this();
setStartDate(startDate);
} // IdentifiableSpecification
/**
* @param startDate
* the starting date that the IdentifiableSpecification's
* contents are valid
* @param endDate
* the ending date that the IdentifiableSpecification's contents
* are valid
*/
protected IdentifiableSpecification(final Date startDate, final Date endDate) {
this(startDate);
setEndDate(endDate);
} // IdentifiableSpecification
/**
* @return the {@link Identifiable} that this instance specifies, creating
* it if necessary.
*/
public final Identifiable getIdentifiable() {
// Have we created the Identifiable yet?
if (identifiable == null) {
// No
identifiable = createIdentifiable();
// Is the Identifiable sane?
if (!identifiable.sane()) {
// No
System.out.println(identifiable.getDublinCore().getTitle()
+ " is not sane!!");
} // if not sane
// We set the title and description last because they might be
// created out of some of the values we just populated.
// Is the Dublin Core title set yet?
if (identifiable.getDublinCore().getTitle() == null
|| identifiable.getDublinCore().getTitle().equals("")) {
// No
identifiable.getDublinCore().setTitle(createDublinCoreTitle());
}
// Is the Dublin Core description set?
if (identifiable.getDublinCore().getDescription() == null
|| identifiable.getDublinCore().getDescription().equals("")) {
// No
identifiable.getDublinCore().setDescription(
createDublinCoreDescription());
}
// We override the coverage, type and subject because some graphs
// compute them for later consumption by the model generation
// process
identifiable.getDublinCore().setCoverage(createDubinCoreCoverage());
identifiable.getDublinCore().setSubject(createDubinCoreSubject());
} // if
return identifiable;
} // getIdentifiable
/**
* @return a new {@link Identifiable} created from the specification.
* @see #createIdentifiableInstance()
*/
protected Identifiable createIdentifiable() {
final Identifiable retValue = createIdentifiableInstance();
retValue.setDublinCore(dublinCore);
retValue.setURI(createPlatformURI());
return retValue;
} // createIdentifiable
/**
* @return an uninitialized instance of {@link Identifiable}
*/
abstract protected Identifiable createIdentifiableInstance();
/**
* @return the dublinCore
*/
public final DublinCore getDublinCore() {
return dublinCore;
}
/**
* @param dublinCore
* the new {@link DublinCore} value to use for the specified
* {@link Identifiable}.
*/
public final void setDublinCore(final DublinCore dublinCore) {
this.dublinCore = dublinCore;
}
/**
* @param endDate
* the endDate to set
*/
public final void setEndDate(final Date endDate) {
dublinCore.setValid(dublinCore.getValidStartDate(), endDate);
} // setEndDate
/**
* @return the endDate
*/
public final Date getEndDate() {
return dublinCore.getValidEndDate();
} // getEndDate
/**
* @return the startDate
*/
public final Date getStartDate() {
return dublinCore.getValidStartDate();
}
/**
* @param startDate
* the startDate to set
*/
public final void setStartDate(final Date startDate) {
dublinCore.setValid(startDate);
} // setStartDate
/**
* @param startDate
* the start date of the date range
* @param endDate
* the end date of the date range
* @return <code>true</code> if the date range of this
* IdentifiableSpecification overlaps the test range,
* <code>false</code> otherwise.
*/
@SuppressWarnings("null")
public boolean inDateRange(final Date startDate, final Date endDate) {
final Date ourStartDate = getStartDate();
final Date ourEndDate = getEndDate();
boolean retValue = (ourStartDate == null || startDate == null)
|| ((ourStartDate.equals(startDate) || ourStartDate
.after(startDate)) && (endDate == null
|| ourStartDate.equals(endDate) || ourStartDate
.before(endDate)));
retValue = retValue
|| (ourStartDate.before(startDate) && (ourEndDate == null
|| ourEndDate.equals(startDate) || ourEndDate
.after(startDate)));
return retValue;
} // inDateRange
/**
* @param identifiableSpecification
* the <code>IdentifiableSpecification</code> to test against
* @return <code>true</code> if the start date of this specification's
* {@link DublinCore} valid date range attribute is before the start
* data of the passed <code>identifiableSpecification</code>'s
* {@link DublinCore} valid date range, or, if
* <code>identifiableSpecification</code> is <code>null</code>,
* <code>false</code> otherwise.
*/
public boolean isValidBefore(
final IdentifiableSpecification identifiableSpecification) {
return getDublinCore().isValidBefore(
identifiableSpecification.getDublinCore());
} // isValidBefore
/**
* Modify the date range to be the most narrow between this
* {@link Identifiable} and the one passed in. For example, if this range is
* from 2000 to 2010 and the other range is from 2005 to 2015, the new range
* would be from 2005 to 2010.
*
* @param identifiableSpecification
* the owner of the date range
*/
protected void updateDateRange(
final IdentifiableSpecification identifiableSpecification) {
final Date[] overLapingValidDateRange = getDublinCore()
.computeValidDateRangeOverlap(
identifiableSpecification.getDublinCore());
// Is there an overlapping range?
if (overLapingValidDateRange != null) {
// Yes
setStartDate(overLapingValidDateRange[0]);
setEndDate(overLapingValidDateRange[1]);
}
} // updateDateRange
// /**
// * @return the number of {@link org.eclipse.stem.core.graph.Node}s in
// * the {@link Identifiable}
// */
// public int getNumNodes() {
// return 0;
// }
//
// /**
// * @return the number of {@link org.eclipse.stem.core.graph.Edge}s in
// * the {@link Identifiable}
// */
// public int getNumEdges() {
// return 0;
// }
//
// /**
// * @return the number of {@link org.eclipse.stem.core.graph.Label}s in
// * the {@link Identifiable}
// */
// public int getNumLabels() {
// return 0;
// }
//
// /**
// * @return the number of {@link Model}
// * {@link org.eclipse.stem.core.model.Decorator}s in the
// * {@link Identifiable}
// */
// public int getNumModelDecorators() {
// return 0;
// } // getNumDecorators
//
// /**
// * @return the number of {@link Scenario}
// * {@link org.eclipse.stem.core.model.Decorator}s in the
// * {@link Identifiable}
// */
// public int getNumScenarioDecorators() {
// return 0;
// }
/**
* @param pluginURI
* @return convert to a resource URI
*/
public static String convertToProjectURI(String pluginURI) {
return pluginURI.replace("plugin", "resource"); //$NON-NLS-1$ //$NON-NLS-2$
}
/**
* @param dc
* @return the project (resource) URI from the plugin URI
*/
public static URI getIdentifiableProjectURI(org.eclipse.stem.internal.data.generatedplugin.DublinCore dc) {
return URI
.createURI(convertToProjectURI(dc
.getIdentifier()));
} // getIdentifiableProjectURI
/**
* @param directory
* this is the root directory that the
* <code>IdentifiableSpecification</code> should serialize the
* {@link Identifiable} it creates. Typically, the file will be
* in a sub-directory.
* @throws IOException
* if there was a problem serializing.
*/
public void serialize(final String directory) throws IOException {
Utility.serializeIdentifiable(getIdentifiable(), converter
.normalize(URI.createFileURI(directory + File.separator
+ getRelativeSerializationPathAndFileName())));
} // serialize
/**
* @return the relative path and file name of the serialization file for the
* {@link Identifiable}. The path begins with a folder name or is
* the name of the file (e.g., "country/USA/USA_0.graph")
*/
protected String getRelativeSerializationPathAndFileName() {
final StringBuilder sb = new StringBuilder(
getRelativeSerializationPath());
sb.append(File.separator);
sb.append(getSerializationFileNameRoot());
sb.append(".");
sb.append(getSerializationFileNameExtension());
return sb.toString();
} // getRelativeSerializationPathAndFileName
/**
* @return the relative path to the folder that will contain the
* serialization file (e.g., "country/USA")
*/
abstract protected String getRelativeSerializationPath();
/**
* @return the name of the file with out the file name extension that is to
* be used to serialize the {@link Identifiable}(e.g., "USA_0")
*/
abstract protected String getSerializationFileNameRoot();
/**
* @return the file name extension to be used when the {@link Identifiable}
* is serialized (e.g., "graph", "model")
* @see IdentifiableSpecification#getSerializationFileName()
*/
abstract protected String getSerializationFileNameExtension();
/**
* @return a short {@link String} that is descriptive of the
* {@link Identifiable}, suitable to be the title in the
* {@link Identifiable}'s {@link DublinCore}.
* @see #createDublinCoreTitle()
*/
abstract protected String getTitleDescriptor();
/**
* Examine the {@link #validValue} and create a string that summarizes it
* suitable for use in a folder name. The idea is that if the range is
* exactly for 1 year, say from January 1, 2006, to December 31, 2006, then
* it should return 2006. If not, then it should concatenate the values
* together to create a unique (more compact) string for the date range.
*
* @param dateFormater
* the formatter that creates the string representation of the
* start and end dates of the data range
*
* @return a String that abstracts the valid date range found in the
* {@link org.eclipse.stem.core.common.DublinCore#getValid()}
* date range specification.
*
* @see #dublinCore
*/
protected String createValidDateRangeString(final DateFormat dateFormater,
final String oneDateRangeFormat, final String twoDatesRangeFormat,
final String justYearRangeFormat) {
String retValue = "";
final String validValue = dublinCore.getValid();
final Date startDate = dublinCore.getValidStartDate();
final Date endDate = dublinCore.getValidEndDate();
// Is there a valid value?
if (validValue != null) {
// Yes
// Is there a start date?
if (startDate != null) {
// Yes
final Calendar startTime = Calendar.getInstance();
startTime.setTime(startDate);
// Is there an end date?
if (endDate != null) {
// Yes
final Calendar endTime = Calendar.getInstance();
endTime.setTime(endDate);
// Do they have the same year?
if (startTime.get(Calendar.YEAR) == endTime
.get(Calendar.YEAR)) {
// Yes
// Is the start date January 1 and the end data December
// 31?
if ((startTime.get(Calendar.MONTH) == Calendar.JANUARY && startTime
.get(Calendar.DAY_OF_MONTH) == 1)
&& (endTime.get(Calendar.MONTH) == Calendar.DECEMBER && endTime
.get(Calendar.DAY_OF_MONTH) == 31)) {
// Yes
// Just use the year as the value
// sb.append(startTime.get(Calendar.YEAR));
retValue = MessageFormat.format(
justYearRangeFormat, new Object[] { Integer
.toString(startTime
.get(Calendar.YEAR)) });
} // if Jan 1 to Dec 31
else {
// No
// Not a full year
retValue = MessageFormat.format(
twoDatesRangeFormat, new Object[] {
dateFormater.format(startDate),
dateFormater.format(endDate) });
} // else
} // If same year
else {
// No
// Not the same year
retValue = MessageFormat.format(twoDatesRangeFormat,
new Object[] { dateFormater.format(startDate),
dateFormater.format(endDate) });
} // else
} // if end date exists
else {
// No
// No end date
retValue = MessageFormat.format(oneDateRangeFormat,
new Object[] { dateFormater.format(startDate) });
}
} // if start date exists
// else nothing
} // if valid value
return retValue;
} // createValidDateRangeString
/**
* @return the {@link URI} that uniquely identifies the {@link Identifiable}
* on the platform.
*/
protected URI createPlatformURI() {
final StringBuilder sb = new StringBuilder(getURIPrefix());
sb.append("/");
sb.append(getRelativeSerializationPathAndFileName().replace('\\', '/'));
return URI.createURI(sb.toString());
} // createGraphPlatformURI
/**
* @return the plugin specific prefix for the {@link Identifiable}
*/
protected String getURIPrefix() {
final StringBuilder sb = new StringBuilder("platform:/plugin/");
sb.append(getTargetPluginId());
sb.append("/resources/data");
return sb.toString();
} // getURIPrefix
/**
* @return the id of the plugin that will contain the {@link Identifiable}
*/
abstract protected String getTargetPluginId();
/**
* Create a title for the {@link Identifiable}
*/
protected String createDublinCoreTitle() {
final String title = dublinCore.getTitle();
final StringBuilder sb = new StringBuilder(title != null ? title
: getTitleDescriptor());
return sb.toString();
} // createTitle
/**
* Create a string that provides a short summary of the contents of the
* <code>Identifiable</code>. This string would be used if a value for the
* Dublin Core description attribute is not provided.
*
* @see org.eclipse.stem.core.common.DublinCore#getDescription()
*/
protected String createDublinCoreDescription() {
return getContentCountString();
} // createDublinCoreDescription
protected String createDubinCoreCoverage() {
return "";
} // createDubinCoreCoverage
protected String createDubinCoreSubject() {
return "";
}
/**
* @return a string that summarizes the number of nodes, edges and labels in
* the {@link Identifiable}
*/
protected String getContentCountString() {
final StringBuilder sb = new StringBuilder();
// final int numAdminLevels = getNumAdminLevels();
// final int numNodes = getNumNodes();
// final int numLabels = getNumLabels();
// final int numEdges = getNumEdges();
// final int numModelDecorators = getNumModelDecorators();
// final int numScenarioDecorators = getNumScenarioDecorators();
//
// final StringBuilder sb2 = new StringBuilder();
// Any Admin levels?
// if (numAdminLevels > 0) {
// // Yes
// sb2.append(numAdminLevels);
// sb2.append(numAdminLevels == 1 ? " Level: " : " Levels: ");
// sb2.append("&quot;");
// // sb2.append(getAdminLevelsAsString(", "));
// sb2.append("&quot;, ");
// } // if admin levels
// final StringBuilder sb3 = new StringBuilder();
//
// // Any Nodes?
// if (numNodes > 0) {
// // Yes
// sb3.append(numNodes);
// sb3.append(numNodes == 1 ? " Node" : " Nodes");
// }
//
// // Any Labels?
// if (numLabels > 0) {
// // Yes
// if (numNodes > 0) {
// sb3.append(", ");
// }
// sb3.append(numLabels);
// sb3.append(numLabels == 1 ? " Label" : " Labels");
// }
//
// // Any Edges?
// if (numEdges > 0) {
// // Yes
// if (numLabels > 0 || numNodes > 0) {
// sb3.append(", ");
// }
// sb3.append(numEdges);
// sb3.append(" Edges");
// }
//
// // Any model Decorators?
// if (numModelDecorators > 0) {
// // Yes
// if (numEdges > 0 || numLabels > 0 || numNodes > 0) {
// sb3.append(", ");
// }
// sb3.append(numModelDecorators);
// sb3.append(numModelDecorators == 1 ? " Model Decorator"
// : " Model Decorators");
// }
//
// // Any scenario Decorators?
// if (numScenarioDecorators > 0) {
// // Yes
// if (numEdges > 0 || numLabels > 0 || numNodes > 0
// || numModelDecorators > 0) {
// sb3.append(", ");
// }
// sb3.append(numScenarioDecorators);
// sb3.append(numScenarioDecorators == 1 ? " Scenario Decorator"
// : " Scenario Decorators");
// }
//
// final String adminString = sb2.toString();
// final String countString = sb3.toString();
//
// sb.append(adminString);
//
// sb.append(countString);
return sb.toString();
} // getContentCountString
} // IdentifiableSpecification