All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jfxtras.icalendarfx.components.VLocatable Maven / Gradle / Ivy

The newest version!
package jfxtras.icalendarfx.components;

import java.time.DateTimeException;
import java.time.LocalDateTime;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAmount;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import jfxtras.icalendarfx.components.VAlarm;
import jfxtras.icalendarfx.components.VComponent;
import jfxtras.icalendarfx.components.VDescribable2;
import jfxtras.icalendarfx.components.VDisplayable;
import jfxtras.icalendarfx.components.VDuration;
import jfxtras.icalendarfx.components.VLocatable;
import jfxtras.icalendarfx.properties.component.descriptive.Description;
import jfxtras.icalendarfx.properties.component.descriptive.GeographicPosition;
import jfxtras.icalendarfx.properties.component.descriptive.Location;
import jfxtras.icalendarfx.properties.component.descriptive.Priority;
import jfxtras.icalendarfx.properties.component.descriptive.Resources;
import jfxtras.icalendarfx.properties.component.time.DurationProp;

public abstract class VLocatable extends VDisplayable implements VDescribable2, VDuration
{
    /**
     * DESCRIPTION
     * RFC 5545 iCalendar 3.8.1.5. page 84
     * 
     * This property provides a more complete description of the
     * calendar component than that provided by the "SUMMARY" property.
     * 
     * Example:
     * DESCRIPTION:Meeting to provide technical review for "Phoenix"
     *  design.\nHappy Face Conference Room. Phoenix design team
     *  MUST attend this meeting.\nRSVP to team leader.
     *
     * Note: Only VJournal allows multiple instances of DESCRIPTION
     */
    @Override
    public Description getDescription() { return description; }
    private Description description;
    @Override
	public void setDescription(Description description)
    {
    	orderChild(this.description, description);
    	this.description = description;
	}

    /** 
     * DURATION
     * RFC 5545 iCalendar 3.8.2.5 page 99, 3.3.6 page 34
     * Can't be used if DTEND is used.  Must be one or the other.
     * 
     * Example:
     * DURATION:PT15M
     * */
    private DurationProp duration;
	@Override
	public DurationProp getDuration() { return duration; }
    @Override
	public void setDuration(DurationProp duration)
    {
        if (duration != null)
        {
            LocalDateTime now = LocalDateTime.now();
            LocalDateTime adjustedNow = now.plus(duration.getValue());
            
            if (adjustedNow.isBefore(now))
            {
                throw new DateTimeException("DURATION is negative (" + duration + "). DURATION MUST be positive.");
            }
        }
        orderChild(this.duration, duration);
        this.duration = duration;
    }
    
    /**
     * GEO: Geographic Position
     * RFC 5545 iCalendar 3.8.1.6 page 85, 3.3.6 page 85
     * This property specifies information related to the global
     * position for the activity specified by a calendar component.
     * 
     * This property value specifies latitude and longitude,
     * in that order (i.e., "LAT LON" ordering).
     * 
     * Example:
     * GEO:37.386013;-122.082932
     */
    private GeographicPosition geographicPosition;
    public GeographicPosition getGeographicPosition() { return geographicPosition; }
    public void setGeographicPosition(GeographicPosition geographicPosition)
    {
    	orderChild(this.geographicPosition, geographicPosition);
    	this.geographicPosition = geographicPosition;
	}
    public void setGeographicPosition(String geographicPosition) { setGeographicPosition(GeographicPosition.parse(geographicPosition)); }
    public void setGeographicPosition(double latitude, double longitude)
    {
        getGeographicPosition().setLatitude(latitude);
        getGeographicPosition().setLongitude(longitude);
    }
    public T withGeographicPosition(GeographicPosition geographicPosition)
    {
        setGeographicPosition(geographicPosition);
        return (T) this;
    }
    public T withGeographicPosition(String geographicPosition)
    {
        setGeographicPosition(geographicPosition);
        return (T) this;
    }
    public T withGeographicPosition(double latitude, double longitude)
    {
        setGeographicPosition(latitude, longitude);
        return (T) this;
    }

    /**
     * LOCATION:
     * RFC 5545 iCalendar 3.8.1.12. page 87
     * This property defines the intended venue for the activity
     * defined by a calendar component.
     * Example:
     * LOCATION:Conference Room - F123\, Bldg. 002
     */
    private Location location;
    public Location getLocation() { return location; }
    public void setLocation(Location location)
    {
    	orderChild(this.location, location);
    	this.location = location;
	}
    public void setLocation(String location) { setLocation(Location.parse(location)); }
    public T withLocation(Location location)
    {
        setLocation(location);
        return (T) this;
    }
    public T withLocation(String location)
    {
        setLocation(location);
        return (T) this;
    }

    /**
     * PRIORITY
     * RFC 5545 iCalendar 3.8.1.6 page 85, 3.3.6 page 85
     * This property defines the relative priority for a calendar component.
     * This priority is specified as an integer in the range 0 to 9.
     * 
     * Example: The following is an example of a property with the highest priority:
     * PRIORITY:1
     */
    private Priority priority;
    public Priority getPriority() { return priority; }
    public void setPriority(Priority priority)
    {
    	orderChild(this.priority, priority);
    	this.priority = priority;
	}
    public void setPriority(String priority) { setPriority(Priority.parse(priority)); }
    public void setPriority(int priority) { setPriority(new Priority(priority)); }
    public T withPriority(Priority priority)
    {
        setPriority(priority);
        return (T) this;
    }
    public T withPriority(String priority)
    {
        setPriority(priority);
        return (T) this;
    }
    public T withPriority(int priority)
    {
        setPriority(priority);
        return (T) this;
    }
    
    /**
     * RESOURCES:
     * RFC 5545 iCalendar 3.8.1.10. page 91
     * This property defines the equipment or resources
     * anticipated for an activity specified by a calendar component.
     * More than one resource can be specified as a COMMA-separated list
     * Example:
     * RESOURCES:EASEL,PROJECTOR,VCR
     * RESOURCES;LANGUAGE=fr:Nettoyeur haute pression
     */
    public List getResources() { return resources; }
    private List resources;
    public void setResources(List resources)
    {
    	if (this.resources != null)
    	{
    		this.resources.forEach(e -> orderChild(e, null)); // remove old elements
    	}
    	this.resources = resources;
    	if (resources != null)
    	{
    		resources.forEach(c -> orderChild(c)); // order new elements
    	}
	}
    public T withResources(List resources)
    {
    	if (getResources() == null)
    	{
    		setResources(new ArrayList<>());
    	}
    	getResources().addAll(resources);
    	if (resources != null)
    	{
    		resources.forEach(c -> orderChild(c));
    	}
        return (T) this;
    }
    public T withResources(String...resources)
    {
        List list = Arrays.stream(resources)
                .map(c -> Resources.parse(c))
                .collect(Collectors.toList());
        return withResources(list);
    }
    public T withResources(Resources...resources)
    {
    	return withResources(Arrays.asList(resources));
    }

    /** 
     * VALARM
     * Alarm Component
     * RFC 5545 iCalendar 3.6.6. page 71
     * 
     * Provide a grouping of component properties that define an alarm.
     * 
     * The "VALARM" calendar component MUST only appear within either a
     * "VEVENT" or "VTODO" calendar component.
     */
    public List getVAlarms() { return vAlarms; }
    private List vAlarms;
    public void setVAlarms(List vAlarms)
    {
    	if (this.vAlarms != null)
    	{
    		this.vAlarms.forEach(e -> orderChild(e, null)); // remove old elements
    	}
    	this.vAlarms = vAlarms;
    	if (vAlarms != null)
    	{
    		vAlarms.forEach(c -> orderChild(c)); // order new elements
    	}
	}
    public T withVAlarms(List vAlarms)
    {
    	if (getVAlarms() == null)
    	{
    		setVAlarms(new ArrayList<>());
    	}
    	if (vAlarms != null)
    	{
    		getVAlarms().addAll(vAlarms);
    		vAlarms.forEach(c -> orderChild(c));
    	}
    	return (T) this;
	}
    public T withVAlarms(VAlarm...vAlarms)
    {
    	withVAlarms(Arrays.asList(vAlarms));
        return (T) this;
        
    }
    public T withVAlarms(String...vAlarms)
    {
        List newElements = Arrays.stream(vAlarms)
                .map(c -> VAlarm.parse(c))
                .collect(Collectors.toList());
        return withVAlarms(newElements);
    }
    
    /*
     * CONSTRUCTORS
     */
    public VLocatable() { super(); }
    
    public VLocatable(VLocatable source)
    {
        super(source);
    }

    @Override
    void addSubcomponent(VComponent subcomponent)
    {
        if (subcomponent instanceof VAlarm)
        {
            final List list;
            if (getVAlarms() == null)
            {
                list = new ArrayList<>();
                setVAlarms(list);
            } else
            {
                list = getVAlarms();
            }
            list.add((VAlarm) subcomponent);
        } else
        {
            throw new IllegalArgumentException("Unspoorted subcomponent type:" + subcomponent.getClass().getSimpleName() +
                    " found inside " + name() + " component");
        }        
    }
    
    @Override
    public List errors()
    {
        List errors = super.errors();
        if (getVAlarms() != null)
        {
            getVAlarms().forEach(v -> errors.addAll(v.errors()));
        }
        boolean isDurationPresent = getDuration() != null;
        if (isDurationPresent) // duration is present
        {
            LocalDateTime now = LocalDateTime.now();
            LocalDateTime adjustedNow = now.plus(getDuration().getValue());
            if (adjustedNow.isBefore(now))
            {
                errors.add("DURATION is negative (" + getDuration().getValue() + "). DURATION MUST be positive.");                
            }
        }
        return errors;
    }
    
    @Override // include VAlarms
    public int hashCode()
    {
        int hash = super.hashCode();
        if (getVAlarms() != null)
        {
            Iterator i = getVAlarms().iterator();
            while (i.hasNext())
            {
                Object property = i.next();
                hash = (31 * hash) + property.hashCode();
            }
        }
        return hash;
    }
    
    /** Stream recurrence dates with adjustment to include recurrences that don't end before start parameter */
    @Override
    public Stream streamRecurrences(Temporal start)
    {
        final TemporalAmount adjustment = getActualDuration();
        return super.streamRecurrences(start.minus(adjustment));
    }
    
    @Override
    public void eraseDateTimeProperties()
    {
        super.eraseDateTimeProperties();
        setDuration((DurationProp) null);
    }
    
    /** A convenience method that returns either Duration property value, or a calculated duration based on start and end values */
    public abstract TemporalAmount getActualDuration();
    
    /**
     * A convenience method that sets DTEND, DURATION (VEvent) or DUE (VTodo) depending on which ever is already set
     * to new value calculated by the duration or period between input parameters (depending on if the parameters
     * are LocalDate or a date/time type (i.e. ZonedDateTime))
     *  
     * Note: In order to set DTEND, DTSTART must be assigned a value.  DURATION and DUE doesn't require a DTSTART value.
     *  
     * @param startRecurrence
     * @param endRecurrence
     */
    public abstract void setEndOrDuration(Temporal startRecurrence, Temporal endRecurrence);
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy