/*
* file: JsonWriter.java
* author: Jon Iles
* copyright: (c) Packwood Software 2015
* date: 18/02/2015
*/
/*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* This library 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 Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
package net.sf.mpxj.json;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.sf.mpxj.AssignmentField;
import net.sf.mpxj.DataType;
import net.sf.mpxj.Duration;
import net.sf.mpxj.FieldContainer;
import net.sf.mpxj.FieldType;
import net.sf.mpxj.Priority;
import net.sf.mpxj.ProjectField;
import net.sf.mpxj.ProjectFile;
import net.sf.mpxj.Relation;
import net.sf.mpxj.Resource;
import net.sf.mpxj.ResourceAssignment;
import net.sf.mpxj.ResourceField;
import net.sf.mpxj.Task;
import net.sf.mpxj.TaskField;
import net.sf.mpxj.TimeUnit;
import net.sf.mpxj.writer.AbstractProjectWriter;
/**
* This class creates a new JSON file from the contents of
* a ProjectFile instance.
*/
public final class JsonWriter extends AbstractProjectWriter
{
/**
* Retrieve the pretty-print flag.
*
* @return true if pretty printing is enabled
*/
public boolean getPretty()
{
return m_pretty;
}
/**
* Set the pretty-print flag.
*
* @param pretty true if pretty printing is enabled
*/
public void setPretty(boolean pretty)
{
m_pretty = pretty;
}
/**
* {@inheritDoc}
*/
@Override public void write(ProjectFile projectFile, OutputStream stream) throws IOException
{
try
{
m_projectFile = projectFile;
m_writer = new JsonStreamWriter(stream);
m_writer.setPretty(m_pretty);
m_writer.writeStartObject(null);
writeProperties();
writeResources();
writeTasks();
writeAssignments();
m_writer.writeEndObject();
m_writer.flush();
}
finally
{
m_projectFile = null;
}
}
/**
* This method writes project property data to a JSON file.
*/
private void writeProperties() throws IOException
{
writeAttributeTypes("property_types", ProjectField.values());
writeFields("property_values", m_projectFile.getProjectProperties(), ProjectField.values());
}
/**
* This method writes resource data to a JSON file.
*/
private void writeResources() throws IOException
{
writeAttributeTypes("resource_types", ResourceField.values());
m_writer.writeStartList("resources");
for (Resource resource : m_projectFile.getAllResources())
{
writeFields(null, resource, ResourceField.values());
}
m_writer.writeEndList();
}
/**
* This method writes task data to a JSON file.
* Note that we write the task hierarchy in order to make rebuilding the hierarchy easier.
*/
private void writeTasks() throws IOException
{
writeAttributeTypes("task_types", TaskField.values());
m_writer.writeStartList("tasks");
for (Task task : m_projectFile.getChildTasks())
{
writeTask(task);
}
m_writer.writeEndList();
}
/**
* This method is called recursively to write a task and its child tasks
* to the JSON file.
*
* @param task task to write
*/
private void writeTask(Task task) throws IOException
{
writeFields(null, task, TaskField.values());
for (Task child : task.getChildTasks())
{
writeTask(child);
}
}
/**
* This method writes assignment data to a JSON file.
*/
private void writeAssignments() throws IOException
{
writeAttributeTypes("assignment_types", AssignmentField.values());
m_writer.writeStartList("assignments");
for (ResourceAssignment assignment : m_projectFile.getAllResourceAssignments())
{
writeFields(null, assignment, AssignmentField.values());
}
m_writer.writeEndList();
}
/**
* Generates a mapping between attribute names and data types.
*
* @param name name of the map
* @param types types to write
*/
private void writeAttributeTypes(String name, FieldType[] types) throws IOException
{
m_writer.writeStartObject(name);
for (FieldType field : types)
{
m_writer.writeNameValuePair(field.name().toLowerCase(), field.getDataType().getValue());
}
m_writer.writeEndObject();
}
/**
* Write a set of fields from a field container to a JSON file.
* @param objectName name of the object, or null if no name required
* @param container field container
* @param fields fields to write
*/
private void writeFields(String objectName, FieldContainer container, FieldType[] fields) throws IOException
{
m_writer.writeStartObject(objectName);
for (FieldType field : fields)
{
Object value = container.getCurrentValue(field);
if (value != null)
{
writeField(field, value);
}
}
m_writer.writeEndObject();
}
/**
* Write the appropriate data for a field to the JSON file based on its type.
*
* @param field field type
* @param value field value
*/
private void writeField(FieldType field, Object value) throws IOException
{
String fieldName = field.name().toLowerCase();
writeField(fieldName, field.getDataType(), value);
}
/**
* Write the appropriate data for a field to the JSON file based on its type.
*
* @param fieldName field name
* @param fieldType field type
* @param value field value
*/
private void writeField(String fieldName, DataType fieldType, Object value) throws IOException
{
switch (fieldType)
{
case INTEGER:
{
writeIntegerField(fieldName, value);
break;
}
case PERCENTAGE:
case CURRENCY:
case NUMERIC:
case UNITS:
{
writeDoubleField(fieldName, value);
break;
}
case BOOLEAN:
{
writeBooleanField(fieldName, value);
break;
}
case WORK:
case DURATION:
{
writeDurationField(fieldName, value);
break;
}
case DATE:
{
writeDateField(fieldName, value);
break;
}
case TIME_UNITS:
{
writeTimeUnitsField(fieldName, value);
break;
}
case PRIORITY:
{
writePriorityField(fieldName, value);
break;
}
case RELATION_LIST:
{
writeRelationList(fieldName, value);
break;
}
case MAP:
{
writeMap(fieldName, value);
break;
}
default:
{
writeStringField(fieldName, value);
break;
}
}
}
/**
* Write an integer field to the JSON file.
*
* @param fieldName field name
* @param value field value
*/
private void writeIntegerField(String fieldName, Object value) throws IOException
{
int val = ((Number) value).intValue();
if (val != 0)
{
m_writer.writeNameValuePair(fieldName, val);
}
}
/**
* Write an double field to the JSON file.
*
* @param fieldName field name
* @param value field value
*/
private void writeDoubleField(String fieldName, Object value) throws IOException
{
double val = ((Number) value).doubleValue();
if (val != 0)
{
m_writer.writeNameValuePair(fieldName, val);
}
}
/**
* Write a boolean field to the JSON file.
*
* @param fieldName field name
* @param value field value
*/
private void writeBooleanField(String fieldName, Object value) throws IOException
{
boolean val = ((Boolean) value).booleanValue();
if (val)
{
m_writer.writeNameValuePair(fieldName, val);
}
}
/**
* Write a duration field to the JSON file.
*
* @param fieldName field name
* @param value field value
*/
private void writeDurationField(String fieldName, Object value) throws IOException
{
if (value instanceof String)
{
m_writer.writeNameValuePair(fieldName + "_text", (String) value);
}
else
{
Duration val = (Duration) value;
if (val.getDuration() != 0)
{
Duration minutes = val.convertUnits(TimeUnit.MINUTES, m_projectFile.getProjectProperties());
long seconds = (long) (minutes.getDuration() * 60.0);
m_writer.writeNameValuePair(fieldName, seconds);
}
}
}
/**
* Write a date field to the JSON file.
*
* @param fieldName field name
* @param value field value
*/
private void writeDateField(String fieldName, Object value) throws IOException
{
if (value instanceof String)
{
m_writer.writeNameValuePair(fieldName + "_text", (String) value);
}
else
{
Date val = (Date) value;
m_writer.writeNameValuePair(fieldName, val);
}
}
/**
* Write a time units field to the JSON file.
*
* @param fieldName field name
* @param value field value
*/
private void writeTimeUnitsField(String fieldName, Object value) throws IOException
{
TimeUnit val = (TimeUnit) value;
if (val != m_projectFile.getProjectProperties().getDefaultDurationUnits())
{
m_writer.writeNameValuePair(fieldName, val.toString());
}
}
/**
* Write a priority field to the JSON file.
*
* @param fieldName field name
* @param value field value
*/
private void writePriorityField(String fieldName, Object value) throws IOException
{
m_writer.writeNameValuePair(fieldName, ((Priority) value).getValue());
}
/**
* Write a map field to the JSON file.
*
* @param fieldName field name
* @param value field value
*/
private void writeMap(String fieldName, Object value) throws IOException
{
@SuppressWarnings("unchecked")
Map<String, Object> map = (Map<String, Object>) value;
m_writer.writeStartObject(fieldName);
for (Map.Entry<String, Object> entry : map.entrySet())
{
Object entryValue = entry.getValue();
if (entryValue != null)
{
DataType type = TYPE_MAP.get(entryValue.getClass().getName());
if (type == null)
{
type = DataType.STRING;
entryValue = entryValue.toString();
}
writeField(entry.getKey(), type, entryValue);
}
}
m_writer.writeEndObject();
}
/**
* Write a string field to the JSON file.
*
* @param fieldName field name
* @param value field value
*/
private void writeStringField(String fieldName, Object value) throws IOException
{
String val = value.toString();
if (!val.isEmpty())
{
m_writer.writeNameValuePair(fieldName, val);
}
}
/**
* Write a relation list field to the JSON file.
*
* @param fieldName field name
* @param value field value
*/
private void writeRelationList(String fieldName, Object value) throws IOException
{
@SuppressWarnings("unchecked")
List<Relation> list = (List<Relation>) value;
m_writer.writeStartList(fieldName);
for (Relation relation : list)
{
m_writer.writeStartObject(null);
writeIntegerField("task_unique_id", relation.getTargetTask().getUniqueID());
writeDurationField("lag", relation.getLag());
writeStringField("type", relation.getType());
m_writer.writeEndObject();
}
m_writer.writeEndList();
}
private ProjectFile m_projectFile;
private JsonStreamWriter m_writer;
private boolean m_pretty;
private static Map<String, DataType> TYPE_MAP = new HashMap<String, DataType>();
static
{
TYPE_MAP.put(Boolean.class.getName(), DataType.BOOLEAN);
TYPE_MAP.put(Date.class.getName(), DataType.DATE);
TYPE_MAP.put(Double.class.getName(), DataType.NUMERIC);
TYPE_MAP.put(Duration.class.getName(), DataType.DURATION);
TYPE_MAP.put(Integer.class.getName(), DataType.INTEGER);
}
}