Skip to content

Commit

Permalink
Added extendable support to comply with RFC 5545 for broken calendars.
Browse files Browse the repository at this point in the history
  • Loading branch information
danielgrigore committed Jan 11, 2017
1 parent 155e8be commit 68844ba
Show file tree
Hide file tree
Showing 29 changed files with 4,921 additions and 138 deletions.
78 changes: 78 additions & 0 deletions src/main/java/net/fortuna/ical4j/model/Calendar.java
Expand Up @@ -33,15 +33,19 @@

import net.fortuna.ical4j.model.component.CalendarComponent;
import net.fortuna.ical4j.model.property.*;
import net.fortuna.ical4j.model.rfc5545.RuleManager;
import net.fortuna.ical4j.util.Strings;
import net.fortuna.ical4j.validate.*;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;

import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.net.URISyntaxException;
import java.text.ParseException;
import java.util.List;
import java.util.Set;

/**
* $Id$ [Apr 5, 2004]
Expand Down Expand Up @@ -345,4 +349,78 @@ public final int hashCode() {
return new HashCodeBuilder().append(getProperties()).append(
getComponents()).toHashCode();
}

@SuppressWarnings("unchecked")
public void conformToRfc5545() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException{

conformPropertiesToRfc5545(properties);

for(Component component : (List<CalendarComponent>)components){
CountableProperties.removeExceededPropertiesForComponent(component);

//each component
conformComponentToRfc5545(component);

//each component property
conformPropertiesToRfc5545(component.getProperties());

for(java.lang.reflect.Method m : component.getClass().getDeclaredMethods()){
if(ComponentList.class.isAssignableFrom(m.getReturnType()) &&
m.getName().startsWith("get")){
List<Component> components = (List<Component>) m.invoke(component);
for(Component c : components){
//each inner component
conformComponentToRfc5545(c);

//each inner component properties
conformPropertiesToRfc5545(c.getProperties());
}
}
}
}
}

private static void conformPropertiesToRfc5545(List<Property> properties) {
for (Property property : properties) {
Set<Rfc5545PropertyRule<Property>> rulesToApply = RuleManager.getSupportedRulesFor(property);
for (Rfc5545PropertyRule<Property> rule : rulesToApply) {
rule.applyTo(property);
}
}
}

private static void conformComponentToRfc5545(Component component){
Set<Rfc5545ComponentRule<Component>> rulesToApply = RuleManager.getSupportedRulesFor(component);
for(Rfc5545ComponentRule<Component> rule : rulesToApply){
rule.applyTo(component);
}
}

private static enum CountableProperties{
STATUS(Property.STATUS, 1);
private int maxApparitionNumber;
private String name;

private CountableProperties(String name, int maxApparitionNumber){
this.maxApparitionNumber = maxApparitionNumber;
this.name = name;
}

protected void limitApparitionsNumberIn(Component component){
PropertyList<? extends Property> propertyList = component.getProperties(name);

if(propertyList.size() <= maxApparitionNumber){
return;
}
int toRemove = propertyList.size() - maxApparitionNumber;
for(int i = 0; i < toRemove; i++){
component.getProperties().remove(propertyList.get(i)); }
}

private static void removeExceededPropertiesForComponent(Component component){
for(CountableProperties cp: values()){
cp.limitApparitionsNumberIn(component);
}
}
}
}
13 changes: 13 additions & 0 deletions src/main/java/net/fortuna/ical4j/model/Rfc5545ComponentRule.java
@@ -0,0 +1,13 @@
package net.fortuna.ical4j.model;

/**
* Incarnation of RFC5545 rule that applies to <code>Component</code> elements.
*
* @author daniel grigore
*
* @param <T>
* subtype of {@link Component} class
*/
public interface Rfc5545ComponentRule<T extends Component> extends Rfc5545Rule<T> {

}
13 changes: 13 additions & 0 deletions src/main/java/net/fortuna/ical4j/model/Rfc5545PropertyRule.java
@@ -0,0 +1,13 @@
package net.fortuna.ical4j.model;

/**
* Incarnation of RFC5545 rule that applies to <code>Property</code> elements.
*
* @author daniel grigore
*
* @param <T>
* subtype of {@link Property} class
*/
public interface Rfc5545PropertyRule<T extends Property> extends Rfc5545Rule<T> {

}
27 changes: 27 additions & 0 deletions src/main/java/net/fortuna/ical4j/model/Rfc5545Rule.java
@@ -0,0 +1,27 @@
package net.fortuna.ical4j.model;

/**
* Incarnation of a RFC5545 rule.
*
* @author daniel grigore
* @author corneliu dobrota
*
* @param <T>
* type of the element this rule can be applied to
*/
public interface Rfc5545Rule<T> {

/**
* Applies this rule to the specified element.
*
* @param element
*/
void applyTo(T element);

/**
* Gets the class of the elements this rule can be applied to.
*
* @return the class of the elements this rule can be applied to
*/
Class<T> getSupportedType();
}
@@ -0,0 +1,50 @@
package net.fortuna.ical4j.model.rfc5545;

import java.net.URI;
import java.net.URISyntaxException;

import org.apache.commons.lang3.StringUtils;

import net.fortuna.ical4j.model.Rfc5545PropertyRule;
import net.fortuna.ical4j.model.property.Attendee;

public class AttendeePropertyRule implements Rfc5545PropertyRule<Attendee> {

private static final String MAILTO = "mailto";
private static final String APOSTROPHE = "'";
private static final int MIN_LENGTH = 3;

@Override
public void applyTo(Attendee element) {
if (element == null) {
return;
}
URI calAddress = element.getCalAddress();
if (calAddress == null) {
return;
}
String scheme = calAddress.getScheme();
if (scheme != null && StringUtils.startsWithIgnoreCase(scheme, MAILTO)) {
String part = calAddress.getSchemeSpecificPart();
if (part != null && part.length() >= MIN_LENGTH && StringUtils.startsWith(part, APOSTROPHE)
&& StringUtils.endsWith(part, APOSTROPHE)) {
String newPart = part.substring(1, part.length() - 1);
safelySetNewValue(element, newPart);
}
}
}

private static void safelySetNewValue(Attendee element, String newPart) {
try {
element.setValue(MAILTO + ":" + newPart);
} catch (URISyntaxException e) {

}
}

@Override
public Class<Attendee> getSupportedType() {
return Attendee.class;
}

}
24 changes: 24 additions & 0 deletions src/main/java/net/fortuna/ical4j/model/rfc5545/DTStampRule.java
@@ -0,0 +1,24 @@
package net.fortuna.ical4j.model.rfc5545;

import net.fortuna.ical4j.model.Rfc5545PropertyRule;
import net.fortuna.ical4j.model.property.DtStamp;

/**
*
* @author daniel grigore
*
*/
public class DTStampRule implements Rfc5545PropertyRule<DtStamp> {

@Override
public void applyTo(DtStamp element) {
if (element.getValue() != null && !element.isUtc()) {
element.setUtc(true);
}
}

@Override
public Class<DtStamp> getSupportedType() {
return DtStamp.class;
}
}
@@ -0,0 +1,24 @@
package net.fortuna.ical4j.model.rfc5545;

import net.fortuna.ical4j.model.Rfc5545PropertyRule;
import net.fortuna.ical4j.model.property.DateListProperty;

/**
*
* @author corneliu dobrota
* @author daniel grigore
*
*/
public class DateListPropertyRule implements Rfc5545PropertyRule<DateListProperty> {

@Override
public void applyTo(DateListProperty element) {
TzHelper.correctTzParameterFrom(element);
}

@Override
public Class<DateListProperty> getSupportedType() {
return DateListProperty.class;
}

}
@@ -0,0 +1,29 @@
package net.fortuna.ical4j.model.rfc5545;

import net.fortuna.ical4j.model.Parameter;
import net.fortuna.ical4j.model.Rfc5545PropertyRule;
import net.fortuna.ical4j.model.property.DateProperty;

/**
*
* @author daniel grigore
* @author corneliu dobrota
*/
public class DatePropertyRule implements Rfc5545PropertyRule<DateProperty> {

@Override
public void applyTo(DateProperty element) {
TzHelper.correctTzParameterFrom(element);
if (!element.isUtc() || element.getParameter(Parameter.TZID) == null) {
return;
}
element.getParameters().removeAll(Parameter.TZID);
element.setUtc(true);
}

@Override
public Class<DateProperty> getSupportedType() {
return DateProperty.class;
}

}
91 changes: 91 additions & 0 deletions src/main/java/net/fortuna/ical4j/model/rfc5545/RuleManager.java
@@ -0,0 +1,91 @@
/*
* Rfc5545RuleManager.java Feb 21, 2014
*
* Copyright (c) 2014 1&1 Internet AG. All rights reserved.
*
* $Id$
*/
package net.fortuna.ical4j.model.rfc5545;

import java.util.LinkedHashSet;
import java.util.Set;

import net.fortuna.ical4j.model.Component;
import net.fortuna.ical4j.model.Property;
import net.fortuna.ical4j.model.Rfc5545ComponentRule;
import net.fortuna.ical4j.model.Rfc5545PropertyRule;
import net.fortuna.ical4j.model.Rfc5545Rule;

/**
* Manages the rules that can be applied to ICS elements. New rules can be simply added by creating new implementations
* of {@link Rfc5545PropertyRule} or {@link Rfc5545ComponentRule}.
*
* @author corneliu dobrota
* @author daniel grigore
* @see Property
* @see Component
*
*/
public class RuleManager {

private static final Set<Rfc5545PropertyRule<? extends Property>> PROPERTY_RULES = new LinkedHashSet<Rfc5545PropertyRule<? extends Property>>();
private static final Set<Rfc5545ComponentRule<? extends Component>> COMPONENT_RULES = new LinkedHashSet<Rfc5545ComponentRule<? extends Component>>();

static {
register(new VAlarmRule());

register(new DatePropertyRule());

register(new DateListPropertyRule());

register(new VEventRule());

register(new TzIdRule());

register(new DTStampRule());

register(new AttendeePropertyRule());
}

private static void register(Rfc5545PropertyRule<? extends Property> rule) {
if (rule.getSupportedType() == null) {
throw new NullPointerException();
}
PROPERTY_RULES.add(rule);
}

private static void register(Rfc5545ComponentRule<? extends Component> rule) {
if (rule.getSupportedType() == null) {
throw new NullPointerException();
}
COMPONENT_RULES.add(rule);
}

@SuppressWarnings("unchecked")
public static Set<Rfc5545PropertyRule<Property>> getSupportedRulesFor(Property element) {
if (element == null) {
throw new NullPointerException();
}
Set<Rfc5545PropertyRule<Property>> rules = new LinkedHashSet<Rfc5545PropertyRule<Property>>(1);
for (Rfc5545Rule<? extends Property> rule : PROPERTY_RULES) {
if (rule.getSupportedType().isInstance(element)) {
rules.add((Rfc5545PropertyRule<Property>) rule);
}
}
return rules;
}

@SuppressWarnings("unchecked")
public static Set<Rfc5545ComponentRule<Component>> getSupportedRulesFor(Component element) {
if (element == null) {
throw new NullPointerException();
}
Set<Rfc5545ComponentRule<Component>> rules = new LinkedHashSet<Rfc5545ComponentRule<Component>>(1);
for (Rfc5545Rule<?> rule : COMPONENT_RULES) {
if (rule.getSupportedType().isInstance(element)) {
rules.add((Rfc5545ComponentRule<Component>) rule);
}
}
return rules;
}
}

0 comments on commit 68844ba

Please sign in to comment.