/*
* � Copyright IBM Corp. 2012
*
* 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.ibm.domino.services.calendar.json;
import static com.ibm.domino.commons.json.JsonConstants.JSON_START;
import static com.ibm.domino.commons.json.JsonConstants.JSON_END;
import static com.ibm.domino.services.calendar.json.JsonConstants.JSON_ALARM;
import static com.ibm.domino.services.calendar.json.JsonConstants.JSON_ATTENDEES;
import static com.ibm.domino.services.calendar.json.JsonConstants.JSON_CATEGORIES;
import static com.ibm.domino.services.calendar.json.JsonConstants.JSON_CLASS;
import static com.ibm.domino.services.calendar.json.JsonConstants.JSON_DESCRIPTION;
import static com.ibm.domino.services.calendar.json.JsonConstants.JSON_DURATION;
import static com.ibm.domino.services.calendar.json.JsonConstants.JSON_EXCEPTDATES;
import static com.ibm.domino.services.calendar.json.JsonConstants.JSON_HREF;
import static com.ibm.domino.services.calendar.json.JsonConstants.JSON_ID;
import static com.ibm.domino.services.calendar.json.JsonConstants.JSON_LASTMODIFIED;
import static com.ibm.domino.services.calendar.json.JsonConstants.JSON_LINKS;
import static com.ibm.domino.services.calendar.json.JsonConstants.JSON_LOCATION;
import static com.ibm.domino.services.calendar.json.JsonConstants.JSON_ORGANIZER;
import static com.ibm.domino.services.calendar.json.JsonConstants.JSON_PRIORITY;
import static com.ibm.domino.services.calendar.json.JsonConstants.JSON_RDATES;
import static com.ibm.domino.services.calendar.json.JsonConstants.JSON_RECURRENCE_ID;
import static com.ibm.domino.services.calendar.json.JsonConstants.JSON_RECURRENCE_RULE;
import static com.ibm.domino.services.calendar.json.JsonConstants.JSON_RELATIONSHIP;
import static com.ibm.domino.services.calendar.json.JsonConstants.JSON_SEQUENCE;
import static com.ibm.domino.services.calendar.json.JsonConstants.JSON_STATUS;
import static com.ibm.domino.services.calendar.json.JsonConstants.JSON_SUMMARY;
import static com.ibm.domino.services.calendar.json.JsonConstants.JSON_TRANSPARENCY;
import static com.ibm.domino.services.calendar.json.JsonConstants.JSON_COMMENT;
import static com.ibm.domino.services.calendar.service.CalendarService.CALENDAR_SERVICE_LOGGER;
import static net.fortuna.ical4j.model.Property.ATTENDEE;
import static net.fortuna.ical4j.model.Property.CATEGORIES;
import static net.fortuna.ical4j.model.Property.DURATION;
import static net.fortuna.ical4j.model.Property.EXDATE;
import static net.fortuna.ical4j.model.Property.LAST_MODIFIED;
import static net.fortuna.ical4j.model.Property.PRIORITY;
import static net.fortuna.ical4j.model.Property.RDATE;
import static net.fortuna.ical4j.model.Property.RRULE;
import static net.fortuna.ical4j.model.Property.COMMENT;
import java.io.IOException;
import java.net.URI;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import net.fortuna.ical4j.model.ComponentList;
import net.fortuna.ical4j.model.Date;
import net.fortuna.ical4j.model.DateTime;
import net.fortuna.ical4j.model.Period;
import net.fortuna.ical4j.model.Property;
import net.fortuna.ical4j.model.PropertyList;
import net.fortuna.ical4j.model.component.VAlarm;
import net.fortuna.ical4j.model.component.VEvent;
import net.fortuna.ical4j.model.property.Categories;
import net.fortuna.ical4j.model.property.Clazz;
import net.fortuna.ical4j.model.property.Comment;
import net.fortuna.ical4j.model.property.DateProperty;
import net.fortuna.ical4j.model.property.Description;
import net.fortuna.ical4j.model.property.DtStart;
import net.fortuna.ical4j.model.property.Duration;
import net.fortuna.ical4j.model.property.ExDate;
import net.fortuna.ical4j.model.property.LastModified;
import net.fortuna.ical4j.model.property.Location;
import net.fortuna.ical4j.model.property.Priority;
import net.fortuna.ical4j.model.property.RDate;
import net.fortuna.ical4j.model.property.RRule;
import net.fortuna.ical4j.model.property.RecurrenceId;
import net.fortuna.ical4j.model.property.Sequence;
import net.fortuna.ical4j.model.property.Status;
import net.fortuna.ical4j.model.property.Summary;
import net.fortuna.ical4j.model.property.Transp;
import net.fortuna.ical4j.model.property.Uid;
import com.ibm.commons.util.io.json.JsonException;
import com.ibm.commons.util.io.json.JsonGenerator.Generator;
import com.ibm.commons.util.io.json.JsonGenerator.StringBuilderGenerator;
import com.ibm.commons.util.io.json.JsonJavaFactory;
import com.ibm.commons.util.io.json.JsonJavaObject;
import com.ibm.commons.util.io.json.JsonObject;
import com.ibm.domino.commons.json.JsonIllegalValueException;
import com.ibm.domino.commons.util.UriHelper;
import com.ibm.domino.services.calendar.util.Utils;
import com.ibm.domino.commons.json.JsonDatePropertyAdapter;
import com.ibm.domino.commons.json.JsonDateTimeRangeAdapter;
/**
* Adapts an iCal4j VEvent object to a JsonObject.
*/
public class JsonEventAdapter implements JsonObject {
private VEvent _event;
private URI _url;
private String[] _properties;
public JsonEventAdapter(VEvent event, URI url) {
_event = event;
_url = url;
}
public Iterator<String> getJsonProperties() {
return new Iterator<String>() {
private int _index = 0;
public boolean hasNext() {
String properties[] = getProperties();
return _index < properties.length ;
}
public String next() {
String properties[] = getProperties();
return properties[_index++];
}
public void remove() {
// The JSON IO classes shouldn't call remove
}
private String[] getProperties() {
if ( _properties != null ) {
return _properties;
}
List<String> properties = new ArrayList<String>();
// The list of properties depends on the event
if ( _url != null ) {
properties.add(JSON_HREF);
}
if ( _event.getUid() != null ) {
properties.add(JSON_ID);
}
if ( _event.getSummary() != null ) {
properties.add(JSON_SUMMARY);
}
if ( _event.getLocation() != null ) {
properties.add(JSON_LOCATION);
}
if ( _event.getDescription() != null ) {
properties.add(JSON_DESCRIPTION);
}
if ( _event.getStartDate() != null ) {
properties.add(JSON_START);
}
if ( _event.getEndDate() != null ) {
properties.add(JSON_END);
}
if ( _event.getProperty(RRULE) != null ) {
properties.add(JSON_RECURRENCE_RULE);
}
if ( _event.getProperty(EXDATE) != null ) {
properties.add(JSON_EXCEPTDATES);
}
if ( _event.getProperty(RDATE) != null ) {
properties.add(JSON_RDATES);
}
if ( _url != null && (_event.getProperty(RRULE) != null || _event.getProperty(RDATE) != null )) {
// The links property is an array of related resources. In this case,
// there is a link to a resource listing the individual instances of
// a recurring event. Filter this property for notice
properties.add(JSON_LINKS);
}
if ( _event.getRecurrenceId() != null ) {
properties.add(JSON_RECURRENCE_ID);
}
if ( _event.getClassification() != null ) {
properties.add(JSON_CLASS);
}
if ( _event.getTransparency() != null ) {
properties.add(JSON_TRANSPARENCY);
}
if ( _event.getStatus() != null ) {
properties.add(JSON_STATUS);
}
if ( _event.getSequence() != null ) {
properties.add(JSON_SEQUENCE);
}
if ( _event.getProperty(LAST_MODIFIED) != null ) {
properties.add(JSON_LASTMODIFIED);
}
if ( _event.getProperty(DURATION) != null ) {
properties.add(JSON_DURATION);
}
if ( _event.getProperty(CATEGORIES) != null ) {
properties.add(JSON_CATEGORIES);
}
if ( _event.getProperty(PRIORITY) != null ) {
properties.add(JSON_PRIORITY);
}
PropertyList attendees = _event.getProperties(ATTENDEE);
if ( attendees != null && attendees.size() > 0 ) {
properties.add(JSON_ATTENDEES);
}
if ( _event.getOrganizer() != null ) {
properties.add(JSON_ORGANIZER);
}
if ( _event.getProperty(COMMENT) != null ) {
properties.add(JSON_COMMENT);
}
//X-properties
PropertyList propertyList = _event.getProperties();
Iterator propertyIterator = propertyList.iterator();
while( propertyIterator.hasNext() ) {
Property tmpProperty = (Property)propertyIterator.next();
if(tmpProperty.getName().startsWith("X-")){ // $NON-NLS-1$
properties.add(tmpProperty.getName().toLowerCase());
}
}
ComponentList alarmList = _event.getAlarms();
if ( alarmList != null && alarmList.size() > 0 ) {
properties.add(JSON_ALARM);
}
// Convert to array
String[] array = new String[properties.size()];
Iterator<String> iterator = properties.iterator();
for ( int i = 0; iterator.hasNext(); i++ ) {
array[i] = iterator.next();
}
// Cache the array for next time
_properties = array;
return array;
}
};
}
/* (non-Javadoc)
* @see com.ibm.commons.util.io.json.JsonObject#getJsonProperty(java.lang.String)
*/
public Object getJsonProperty(String property) {
// This method is called when converting iCalendar to JSON
if ( JSON_SUMMARY.equals(property)) {
return Utils.getUnescapedString(_event.getSummary());
}
else if ( JSON_LOCATION.equals(property) ) {
return Utils.getUnescapedString(_event.getLocation());
}
else if ( JSON_ID.equals(property) ) {
return _event.getUid().getValue();
}
else if ( JSON_DESCRIPTION.equals(property) ) {
return Utils.getUnescapedString(_event.getDescription());
}
else if ( JSON_START.equals(property) ) {
DateProperty dp = _event.getStartDate();
JsonDatePropertyAdapter adapter = new JsonDatePropertyAdapter(dp);
return adapter;
}
else if ( JSON_END.equals(property) ) {
DateProperty dp = _event.getEndDate();
JsonDatePropertyAdapter adapter = new JsonDatePropertyAdapter(dp);
return adapter;
}
else if ( JSON_HREF.equals(property) ) {
return _url.toString();
}
else if ( JSON_RECURRENCE_RULE.equals(property) ) {
Property rruleProp = _event.getProperty(RRULE);
return rruleProp.getValue();
}
else if ( JSON_EXCEPTDATES.equals(property) ) {
List<JsonDatePropertyAdapter> adapters = new ArrayList<JsonDatePropertyAdapter>();
Property exdateProperty = _event.getProperty(EXDATE);
if ( exdateProperty instanceof ExDate ) {
ExDate exdate = (ExDate)exdateProperty;
Iterator<Date> iterator = exdate.getDates().iterator();
while (iterator.hasNext()) {
adapters.add(new JsonDatePropertyAdapter(new DtStart(iterator.next())));
}
}
return adapters;
}
else if ( JSON_RDATES.equals(property) ) {
List<JsonDateTimeRangeAdapter> adapters = new ArrayList<JsonDateTimeRangeAdapter>();
Property rdateProp = _event.getProperty(RDATE);
if ( rdateProp instanceof RDate ) {
RDate rdate = (RDate)rdateProp;
Iterator<Period> iterator = rdate.getPeriods().iterator();
while (iterator.hasNext()) {
adapters.add(new JsonDateTimeRangeAdapter(iterator.next()));
}
}
return adapters;
}
else if ( JSON_LINKS.equals(property) ) {
List<JsonJavaObject> links = new ArrayList<JsonJavaObject>();
JsonJavaObject instances = new JsonJavaObject();
instances.putJsonProperty(JSON_RELATIONSHIP, "instances"); // $NON-NLS-1$
URI uri = UriHelper.appendPathSegment(_url, "instances"); // $NON-NLS-1$
instances.putJsonProperty(JSON_HREF, uri.toString());
links.add(instances);
return links;
}
else if ( JSON_RECURRENCE_ID.equals(property) ) {
return _event.getRecurrenceId().getValue();
}
else if ( JSON_TRANSPARENCY.equals(property) ) {
return _event.getTransparency().getValue().toLowerCase();
}
else if ( JSON_CLASS.equals(property) ) {
return _event.getClassification().getValue().toLowerCase();
}
else if ( JSON_STATUS.equals(property) ) {
return _event.getStatus().getValue().toLowerCase();
}
else if ( JSON_SEQUENCE.equals(property) ) {
return _event.getSequence().getSequenceNo();
}
else if ( JSON_ORGANIZER.equals(property) ) {
return new JsonAttendeeAdapter(_event.getOrganizer());
}
else if ( JSON_ATTENDEES.equals(property) ) {
List<JsonAttendeeAdapter> attendees = new ArrayList<JsonAttendeeAdapter>();
PropertyList list = _event.getProperties(ATTENDEE);
if ( list != null ) {
Iterator iterator = list.iterator();
while (iterator.hasNext() ) {
attendees.add(new JsonAttendeeAdapter((Property)iterator.next()));
}
}
return attendees;
}
else if ( JSON_ALARM.equals(property) ) {
List<JsonAlarmAdapter> alarms = new ArrayList<JsonAlarmAdapter>();
ComponentList list = _event.getAlarms();
if ( list != null ) {
Iterator iterator = list.iterator();
while (iterator.hasNext() ) {
VAlarm alarmTmp=(VAlarm)iterator.next();
alarms.add(new JsonAlarmAdapter(alarmTmp));
}
}
return alarms;
}
else if ( JSON_LASTMODIFIED.equals(property)) {
// The property value MUST be specified in the UTC time format.
return _event.getLastModified().getValue();
}
else if ( JSON_DURATION.equals(property) ) {
return _event.getProperty(DURATION).getValue();
}
else if ( JSON_CATEGORIES.equals(property) ) {
// As the core api represent Categories like CATEGORIES:Projects;Phone Calls but not CATEGORIES:Projects,Phone Calls
// ical4j can't identify these are two values. So we must represent them as one value
// Actually we'd better use Categories.getCategories();
return Utils.getUnescapedString(_event.getProperty(CATEGORIES));
}
else if ( JSON_PRIORITY.equals(property) ) {
return _event.getPriority().getLevel();
}
else if(JSON_COMMENT.equals(property)){
return Utils.getUnescapedString(_event.getProperty(COMMENT));
}
else {
// X- property
Property tmpProperty = _event.getProperty(property.toUpperCase());
if(tmpProperty!=null){
return new JsonXPropertyAdapter(tmpProperty);
}
return null;
}
}
/* (non-Javadoc)
* @see com.ibm.commons.util.io.json.JsonObject#putJsonProperty(java.lang.String, java.lang.Object)
*/
public void putJsonProperty(String propertyName, Object value) {
// This method is called when converting JSON to iCalendar
Property property = null;
try {
if ( JSON_SUMMARY.equals(propertyName) ) {
property = new Summary((String)value);
}
else if ( JSON_LOCATION.equals(propertyName) ) {
property = new Location((String)value);
}
else if ( JSON_ID.equals(propertyName) ) {
property = new Uid((String)value);
}
else if ( JSON_RECURRENCE_ID.equals(propertyName) ) {
String recurID = (String)value;
if(recurID.indexOf("T")>0){
property = new RecurrenceId(new DateTime((String)value));
}
else{
property = new RecurrenceId(new Date((String)value));
}
}
else if ( JSON_DESCRIPTION.equals(propertyName) ) {
property = new Description((String)value);
}
else if ( JSON_TRANSPARENCY.equals(propertyName) ) {
property = new Transp(((String)value).toUpperCase());
}
else if ( JSON_CLASS.equals(propertyName) ) {
property = new Clazz(((String)value).toUpperCase());
}
else if ( JSON_STATUS.equals(propertyName) ) {
property = new Status(((String)value).toUpperCase());
}
else if ( JSON_LASTMODIFIED.equals(propertyName) ) {
property = new LastModified(new DateTime((String)value));
}
else if ( JSON_SEQUENCE.equals(propertyName) ) {
property = new Sequence(((Double)value).intValue());
}
else if ( JSON_RECURRENCE_RULE.equals(propertyName) ) {
property = new RRule(null, (String)value);
}
else if ( JSON_ALARM.equals(propertyName)) {
if ( value instanceof List<?> ) {
Iterator<?> iterator = ((List<?>)value).iterator();
while (iterator.hasNext()) {
Object item = iterator.next();
if ( item instanceof JsonAlarmAdapter) {
JsonAlarmAdapter adapter = (JsonAlarmAdapter)item;
_event.getAlarms().add(adapter.compose(propertyName));
}
}
}
}
else if ( JSON_DURATION.equals(propertyName) ) {
property = new Duration();
((Duration)property).setValue((String)value);
}
else if ( JSON_CATEGORIES.equals(propertyName) ) {
property = new Categories(((String)value));
}
else if ( JSON_PRIORITY.equals(propertyName) ) {
property = new Priority(((Double)value).intValue());
}
else if ( JSON_COMMENT.equals(propertyName) ) {
property = new Comment((String)value);
}
else if ( propertyName.toUpperCase().startsWith("X-")) { // $NON-NLS-1$
if ( value instanceof JsonXPropertyAdapter ) {
JsonXPropertyAdapter adapter = (JsonXPropertyAdapter)value;
_event.getProperties().add(adapter.compose(propertyName));
}
else if ( value instanceof List<?> ) {
Iterator<?> iterator = ((List<?>)value).iterator();
while (iterator.hasNext()) {
Object item = iterator.next();
if ( item instanceof JsonXPropertyAdapter) {
JsonXPropertyAdapter adapter = (JsonXPropertyAdapter)item;
_event.getProperties().add(adapter.compose(propertyName));
}
}
}
}
if ( property != null ) {
_event.getProperties().add(property);
}
}
catch (ParseException e) {
CALENDAR_SERVICE_LOGGER.getLogger().fine(e.getMessage());
throw new JsonIllegalValueException(e);
}
}
public VEvent getEvent() {
return _event;
}
/**
* Get a list of event instances in JSON format.
*
* <p>TODO: Move this method to another class. It's only here now for convenience.
*
* @param list
* @param baseUrl
* @return
* @throws IOException
* @throws JsonException
*/
public static String getInstances(ArrayList<String> list, URI baseUrl) throws IOException, JsonException {
StringBuilder sb = new StringBuilder();
Generator generator = new StringBuilderGenerator(JsonJavaFactory.instanceEx, sb, false);
generator.out("{");
generator.nl();
generator.incIndent();
generator.indent();
generator.outPropertyName("instances"); // $NON-NLS-1$
generator.out(":[");
generator.nl();
generator.incIndent();
String recurrenceId = null;
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
recurrenceId = iterator.next();
generator.indent();
generator.out("{");
generator.incIndent();
generator.nl();
generator.indent();
generator.outPropertyName(JSON_RECURRENCE_ID);
generator.out(":");
generator.outLiteral(recurrenceId);
generator.out(",");
generator.nl();
generator.indent();
generator.outPropertyName(JSON_HREF);
generator.out(":");
URI instacneURI = UriHelper.appendPathSegment(baseUrl, recurrenceId);
generator.outLiteral(instacneURI.toString());
generator.nl();
generator.decIndent();
generator.indent();
generator.out("}");
if(iterator.hasNext()){
generator.out(",");
}
generator.nl();
}
generator.decIndent();
generator.indent();
generator.out("]");
generator.decIndent();
generator.nl();
generator.indent();
generator.out("}");
return sb.toString();
}
}