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

jfxtras.icalendarfx.VCalendar Maven / Gradle / Ivy

There is a newer version: 17-r1
Show newest version
package jfxtras.icalendarfx;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;

import jfxtras.icalendarfx.VCalendar;
import jfxtras.icalendarfx.VParentBase;
import jfxtras.icalendarfx.components.VComponent;
import jfxtras.icalendarfx.components.VEvent;
import jfxtras.icalendarfx.components.VFreeBusy;
import jfxtras.icalendarfx.components.VJournal;
import jfxtras.icalendarfx.components.VPersonal;
import jfxtras.icalendarfx.components.VTimeZone;
import jfxtras.icalendarfx.components.VTodo;
import jfxtras.icalendarfx.content.MultiLineContent;
import jfxtras.icalendarfx.content.OrdererBase;
import jfxtras.icalendarfx.content.UnfoldingStringIterator;
import jfxtras.icalendarfx.itip.AbstractITIPFactory;
import jfxtras.icalendarfx.itip.DefaultITIPFactory;
import jfxtras.icalendarfx.itip.Processable;
import jfxtras.icalendarfx.properties.calendar.CalendarScale;
import jfxtras.icalendarfx.properties.calendar.Method;
import jfxtras.icalendarfx.properties.calendar.ProductIdentifier;
import jfxtras.icalendarfx.properties.calendar.Version;
import jfxtras.icalendarfx.properties.calendar.Method.MethodType;
import jfxtras.icalendarfx.properties.component.misc.NonStandardProperty;
import jfxtras.icalendarfx.properties.component.misc.RequestStatus;
import jfxtras.icalendarfx.utilities.DateTimeUtilities;

/**
 * iCalendar Object
 * RFC 5545, 3.4, page 50
 * 
 * Parent calendar object represents a collection of calendaring and scheduling information 
 * 
 * @author David Bal
 *
 */
public class VCalendar extends VParentBase
{
    // version of this project, not associated with the iCalendar specification version
//    public static String myVersion = "1.0";
    private static final String NAME = "VCALENDAR";
    @Override public String name() { return NAME; }
    private static final String FIRST_CONTENT_LINE = BEGIN + NAME;
    private static final String LAST_CONTENT_LINE = END + NAME;
    
    /*
     * Calendar properties
     */
    
    /**
     *  CALSCALE: RFC 5545 iCalendar 3.7.1. page 76
     * This property defines the calendar scale used for the
     * calendar information specified in the iCalendar object.
     * 
     * This project is based on the Gregorian calendar scale.
     * The Gregorian calendar scale is assumed if this property is not
     * specified in the iCalendar object.  It is expected that other
     * calendar scales will be defined in other specifications or by
     * future versions of iCalendar.
     * 
     * The default value is "GREGORIAN"
     * 
     * Example:
     * CALSCALE:GREGORIAN
     * */
    public CalendarScale getCalendarScale() { return calendarScale; }
    private CalendarScale calendarScale;
    public void setCalendarScale(String calendarScale) { setCalendarScale(CalendarScale.parse(calendarScale)); }
    public void setCalendarScale(CalendarScale calendarScale)
    {
    	this.calendarScale = calendarScale;
    	orderChild(calendarScale);
	}
    public VCalendar withCalendarScale(CalendarScale calendarScale)
    {
        setCalendarScale(calendarScale);
        return this;
    }
    public VCalendar withCalendarScale(String calendarScale)
    {
        setCalendarScale(calendarScale);
        return this;
    }

    /**
     * METHOD: RFC 5545 iCalendar 3.7.2. page 77
     * This property defines the iCalendar object method
     * associated with the calendar object
     * 
     * Example:
     * METHOD:REQUEST
     * */
    public Method getMethod() { return method; }
    private Method method;
    public void setMethod(String method) { setMethod(Method.parse(method)); }
    public void setMethod(Method method)
    {
    	this.method = method;
    	orderChild(method);
	}
    public void setMethod(MethodType method) { setMethod(new Method(method)); }
    public VCalendar withMethod(Method method)
    {
        setMethod(method);
        return this;
    }
    public VCalendar withMethod(MethodType method)
    {
        setMethod(method);
        return this;
    }
    public VCalendar withMethod(String method)
    {
        setMethod(method);
        return this;
    }
    
    /**
     * PRODID: RFC 5545 iCalendar 3.7.3. page 78
     * This property specifies the identifier for the product that
     * created the iCalendar object
     * 
     * This project is named JFxtras iCalendar
     * 
     * Example:
     * PRODID:-//JFxtras//JFXtras iCalendar 1.0//EN
     * */
    public ProductIdentifier getProductIdentifier() { return productIdentifier; }
    private ProductIdentifier productIdentifier;
    public void setProductIdentifier(String productIdentifier) { setProductIdentifier(ProductIdentifier.parse(productIdentifier)); }
    public void setProductIdentifier(ProductIdentifier productIdentifier)
    {
    	this.productIdentifier = productIdentifier;
    	orderChild(productIdentifier);
	}
    public VCalendar withProductIdentifier(ProductIdentifier productIdentifier)
    {
        setProductIdentifier(productIdentifier);
        return this;
    }
    public VCalendar withProductIdentifier(String productIdentifier)
    {
        setProductIdentifier(productIdentifier);
        return this;
    }
    
    /**
     *  VERSION: RFC 5545 iCalendar 3.7.4. page 79
     * This property specifies the identifier corresponding to the
     * highest version number or the minimum and maximum range of the
     * iCalendar specification that is required in order to interpret the
     * iCalendar object.
     * 
     * This project complies with version 2.0
     * 
     * Example:
     * VERSION:2.0
     * */
    public Version getVersion() { return version; }
    private Version version;
    public void setVersion(String version) { setVersion(Version.parse(version)); }
    public void setVersion(Version version)
    {
    	this.version = version;
    	orderChild(version);
	}
    public VCalendar withVersion(Version version)
    {
        setVersion(version);
        return this;
    }
    public VCalendar withVersion(String version)
    {
        setVersion(version);
        return this;
    }
    public VCalendar withVersion()
    {
        setVersion(new Version());
        return this;
    }

    /**
     * Provides a framework for defining non-standard properties.
     */
    private List nonStandardProps;
    public List getNonStandard() { return nonStandardProps; }
    public void setNonStandard(List nonStandardProps)
    {
    	if (this.nonStandardProps != null)
    	{
    		this.nonStandardProps.forEach(e -> orderChild(e, null)); // remove old elements
    	}
    	this.nonStandardProps = nonStandardProps;
    	if (nonStandardProps != null)
    	{
    		nonStandardProps.forEach(c -> orderChild(c)); // order new elements
    	}
	}
    /**
     * Sets the value of the {@link #nonStandardProperty()}
     * 
     * @return - this class for chaining
     */
    public VCalendar withNonStandard(List nonStandardProps)
    {
    	if (getNonStandard() == null)
    	{
        	setNonStandard(new ArrayList<>());
    	}
    	getNonStandard().addAll(nonStandardProps);
    	if (nonStandardProps != null)
    	{
    		nonStandardProps.forEach(c -> orderChild(c));
    	}
        return this;
    }
    /**
     * Sets the value of the {@link #nonStandardProperty()} by parsing a vararg of
     * iCalendar content text representing individual {@link NonStandardProperty} objects.
     * 
     * @return - this class for chaining
     */
    public VCalendar withNonStandard(String...nonStandardProps)
    {
        List newElements = Arrays.stream(nonStandardProps)
                .map(c -> NonStandardProperty.parse(c))
                .collect(Collectors.toList());
        return withNonStandard(newElements);
    }
    /**
     * Sets the value of the {@link #nonStandardProperty()} from a vararg of {@link NonStandardProperty} objects.
     * 
     * @return - this class for chaining
     */    
    public VCalendar withNonStandard(NonStandardProperty...nonStandardProps)
    {
    	return withNonStandard(Arrays.asList(nonStandardProps));
    }
   
    /*
     * Calendar Components
     */

    /** 
     * VEVENT: RFC 5545 iCalendar 3.6.1. page 52
     * 
     * A grouping of component properties that describe an event.
     * 
     */
    public List getVEvents() { return vEvents; }
    private List vEvents;
    public void setVEvents(List vEvents)
    {
    	if (this.vEvents != null)
    	{
    		this.vEvents.forEach(e -> orderChild(e, null)); // remove old elements
    	}
    	this.vEvents = vEvents;
    	if (vEvents != null)
		{
    		vEvents.forEach(c -> orderChild(c)); // order new elements
		}
	}
    public VCalendar withVEvents(List vEvents)
    {
    	if (getVEvents() == null)
    	{
    		setVEvents(new ArrayList<>());
    	}
    	getVEvents().addAll(vEvents);
    	if (vEvents != null)
    	{
    		vEvents.forEach(c -> orderChild(c));
    	}
        return this;
    }
    public VCalendar withVEvents(String...vEvents)
    {
        List list = Arrays.stream(vEvents)
                .map(c -> VEvent.parse(c))
                .collect(Collectors.toList());
        return withVEvents(list);
    }
    public VCalendar withVEvents(VEvent...vEvents)
    {
        return withVEvents(Arrays.asList(vEvents));
    }
  
    /** 
     * VTODO: RFC 5545 iCalendar 3.6.2. page 55
     * 
     * A grouping of component properties that describe a task that needs to be completed.
     * 
     */
    public List getVTodos() { return vTodos; }
    private List vTodos;
    public void setVTodos(List vTodos)
    {
    	if (this.vTodos != null)
    	{
    		this.vTodos.forEach(e -> orderChild(e, null)); // remove old elements
    	}
    	this.vTodos = vTodos;
    	if (vTodos != null)
		{
    		vTodos.forEach(c -> orderChild(c)); // order new elements
		}
	}
    public VCalendar withVTodos(List vTodos)
    {
    	if (getVTodos() == null)
    	{
    		setVTodos(new ArrayList<>());
    	}
    	getVTodos().addAll(vTodos);
    	if (vTodos != null)
    	{
    		vTodos.forEach(c -> orderChild(c));
    	}
        return this;
    }
    public VCalendar withVTodos(String...vTodos)
    {
        List list = Arrays.stream(vTodos)
                .map(c -> VTodo.parse(c))
                .collect(Collectors.toList());
        return withVTodos(list);
    }
    public VCalendar withVTodos(VTodo...vTodos)
    {
    	return withVTodos(Arrays.asList(vTodos));
    }
 
    /** 
     * VJOURNAL: RFC 5545 iCalendar 3.6.3. page 57
     * 
     * A grouping of component properties that describe a task that needs to be completed.
     * 
     * @see VComponent
     * @see VJournal
     */
    public List getVJournals() { return vJournals; }
    private List vJournals;
    public void setVJournals(List vJournals)
    {
    	if (this.vJournals != null)
    	{
    		this.vJournals.forEach(e -> orderChild(e, null)); // remove old elements
    	}
    	this.vJournals = vJournals;
    	if (vJournals != null)
		{
    		vJournals.forEach(c -> orderChild(c)); // order new elements
		}
	}
    public VCalendar withVJournals(List vJournals)
    {
    	if (getVJournals() == null)
    	{
    		setVJournals(new ArrayList<>());
    	}
    	getVJournals().addAll(vJournals);
    	if (vJournals != null)
    	{
    		vJournals.forEach(c -> orderChild(c));
    	}
        return this;
	}
    public VCalendar withVJournals(String...vJournals)
    {
        List list = Arrays.stream(vJournals)
                .map(c -> VJournal.parse(c))
                .collect(Collectors.toList());
        return withVJournals(list);
    }
    public VCalendar withVJournals(VJournal...vJournals)
    {
    	return withVJournals(Arrays.asList(vJournals));
    }

    /** 
     * VFREEBUSY: RFC 5545 iCalendar 3.6.4. page 59
     * 
     * @see VFreeBusy
     */
    public List getVFreeBusies() { return vFreeBusys; }
    private List vFreeBusys;
    public void setVFreeBusys(List vFreeBusys)
    {
    	if (this.vFreeBusys != null)
    	{
    		this.vFreeBusys.forEach(e -> orderChild(e, null)); // remove old elements
    	}
    	this.vFreeBusys = vFreeBusys;
    	if (vFreeBusys != null)
		{
    		vFreeBusys.forEach(c -> orderChild(c)); // order new elements
		}
	}
    public VCalendar withVFreeBusies(List vFreeBusys)
    {
    	if (getVFreeBusies() == null)
    	{
    		setVFreeBusys(new ArrayList<>());
    	}
    	getVFreeBusies().addAll(vFreeBusys);
    	if (vFreeBusys != null)
    	{
    		vFreeBusys.forEach(c -> orderChild(c));
    	}
        return this;
	}
    public VCalendar withVFreeBusies(String...vFreeBusys)
    {
    	List list = Arrays.stream(vFreeBusys)
    			.map(c -> VFreeBusy.parse(c))
                .collect(Collectors.toList());
    	return withVFreeBusies(list);
    }
    public VCalendar withVFreeBusies(VFreeBusy...vFreeBusys)
    {
    	return withVFreeBusies(Arrays.asList(vFreeBusys));
    }

    /** 
     * VTIMEZONE: RFC 5545 iCalendar 3.6.5. page 62
     * 
     * @see VTimeZone
     */
    public List getVTimeZones() { return vTimeZones; }
    private List vTimeZones;
    public void setVTimeZones(List vTimeZones)
    {
    	if (this.vTimeZones != null)
    	{
    		this.vTimeZones.forEach(e -> orderChild(e, null)); // remove old elements
    	}
    	this.vTimeZones = vTimeZones;
    	if (vTimeZones != null)
		{
    		vTimeZones.forEach(c -> orderChild(c)); // order new elements
		}
	}
    public VCalendar withVTimeZones(List vTimeZones)
    {
    	if (getVTimeZones() == null)
    	{
    		setVTimeZones(new ArrayList<>());
    	}
    	getVTimeZones().addAll(vTimeZones);
    	if (vTimeZones != null)
    	{
    		vTimeZones.forEach(c -> orderChild(c));
    	}
        return this;
	}
    public VCalendar withVTimeZones(String...vTimeZones)
    {
    	List list = Arrays.stream(vTimeZones)
    			.map(c -> VTimeZone.parse(c))
                .collect(Collectors.toList());
    	return withVTimeZones(list);
    }
    public VCalendar withVTimeZones(VTimeZone...vTimeZones)
    {
    	return withVTimeZones(Arrays.asList(vTimeZones));
    }
    
//    /**
//     * A convenience method that adds a VComponent to one of the Lists based on
//     * its type such as VEVENT, VTODO, etc.
//     * 
//     * @param newVComponent - VComponent to add
//     * @return  true if add was successful, false otherwise
//     */
//    @Deprecated
//    public boolean addVComponent(VComponent newVComponent)
//    {
//        if (newVComponent instanceof VEvent)
//        {
//        	System.out.println("getVEvents():" + getVEvents());
//            getVEvents().add((VEvent) newVComponent);
//        } else if (newVComponent instanceof VTodo)
//        {
//            getVTodos().add((VTodo) newVComponent);            
//        } else if (newVComponent instanceof VJournal)
//        {
//            getVJournals().add((VJournal) newVComponent);
//        } else if (newVComponent instanceof VFreeBusy)
//        {
//            getVFreeBusies().add((VFreeBusy) newVComponent);            
//        } else if (newVComponent instanceof VTimeZone)
//        {
//            getVTimeZones().add((VTimeZone) newVComponent);            
//        } else
//        {
//            throw new RuntimeException("Unsuppored VComponent type:" + newVComponent.getClass());
//        }
//        orderChild(newVComponent);
//        return true;
//    }
//    
//    public boolean removeVComponent(VComponent vComponent)
//    {
//        if (vComponent instanceof VEvent)
//        {
//            return getVEvents().remove(vComponent);
//        } else if (vComponent instanceof VTodo)
//        {
//            return getVTodos().remove(vComponent);            
//        } else if (vComponent instanceof VJournal)
//        {
//            return getVJournals().remove(vComponent);
//        } else if (vComponent instanceof VFreeBusy)
//        {
//            return getVFreeBusies().remove(vComponent);            
//        } else if (vComponent instanceof VTimeZone)
//        {
//            return getVTimeZones().remove(vComponent);            
//        } else
//        {
//            throw new RuntimeException("Unsuppored VComponent type:" + vComponent.getClass());
//        }
//    }
       
    /**
     * A convenience method that returns parent list of the {@link VComponent} parameter.
     * Returns null if component is not in any {@link VComponent} list.
     * 
     * @param vComponent - VComponent to look up
     */
    public List getVComponents(VComponent vComponent)
    {
        if (vComponent instanceof VEvent)
        {
            return getVEvents();
        } else if (vComponent instanceof VTodo)
        {
            return getVTodos();
        } else if (vComponent instanceof VJournal)
        {
            return getVJournals();
        } else if (vComponent instanceof VFreeBusy)
        {
            return getVFreeBusies();
        } else if (vComponent instanceof VTimeZone)
        {
            return getVTimeZones();
        } else
        {
            throw new RuntimeException("Unsuppored VComponent type:" + vComponent.getClass());
        }
    }
    
    
    /** set AbstractITIPFactory to handle processing input VCalendar based on {@link Method} */
    public void setMethodProcessFactory(AbstractITIPFactory iTIPFactory)
    {
        this.iTIPFactory = iTIPFactory;
    }
    /** get AbstractITIPFactory to handle processing input VCalendar based on {@link Method} */
    public AbstractITIPFactory getITIPFactory()
    {
        return iTIPFactory;
    }
    private AbstractITIPFactory iTIPFactory;
    
    /**
     * Process the exchange of iCalendar object according to the iTIP methods identifies in RFC 5546
     * based on the methods in {@link #getITIPFactory()}
     * 
     * @param iTIPMessages  iTIP VCalendars to process with {@link Method} populated
     * @return - log of process operation
     */
    public List processITIPMessage(VCalendar... iTIPMessages)
    {
        return processITIPMessage(Arrays.asList(iTIPMessages));
    }
    
    /**
     * Process the exchange of iCalendar object according to the iTIP methods identifies in RFC 5546
     * based on the methods in {@link #getITIPFactory()}
     * 
     * @param iTIPMessages  iTIP VCalendars to process with {@link Method} populated
     * @return - log of process operation
     */
    public List processITIPMessage(Collection iTIPMessages)
    {
        List log = new ArrayList<>();
        iTIPMessages.forEach(message ->
        {
            final Processable methodProcess;
            if (message.getMethod() == null)
            { // default to PUBLISH method if not present
                methodProcess = getITIPFactory().getITIPMessageProcess(MethodType.PUBLISH);
 //               throw new IllegalArgumentException("VCalendar to be processed MUST have the METHOD property populated");
            } else
            {
                MethodType method = message.getMethod().getValue();
                methodProcess = getITIPFactory().getITIPMessageProcess(method);
            }
            List methodLog = methodProcess.process(this, message);
            log.addAll(methodLog);
        });
        return log;
    }
    
    /**
     * Process the exchange of iCalendar object according to the iTIP methods identifies in RFC 5546.
     * Input string can contain multiple iTIP VCALENDAR messages.
     * 
     * @param iTIPMessages  iTIP VCalendar Message strings
     * @return - log of process operation
     */
    public List processITIPMessage(String iTIPMessages)
    {
        List log = new ArrayList<>();
        List iTIPMessageList = new ArrayList<>();
        StringBuilder builder = new StringBuilder(1000);
        for(String line : iTIPMessages.split(System.lineSeparator()))
        {
            if (line.equals("BEGIN:VCALENDAR") && builder.length() > 0)
            {
                iTIPMessageList.add(builder.toString());
                builder = new StringBuilder(1000);
            }
            builder.append(line + System.lineSeparator());
        }
        // handle last VCalendar message
        if (builder.length() > 0)
        {
            iTIPMessageList.add(builder.toString());
        }
        iTIPMessageList.forEach(message -> 
        {
            List methodLog = processITIPMessage(VCalendar.parse(message));
            log.addAll(methodLog);
        });
        return log;
    }
    
    /**
     * Parse component text to new VComponent with {@link RequestStatus REQUEST-STATUS} properties containing 
     * the result of the process, such as success message or error report.
     * 
     * @param contentText  iCalendar content lines
     * @return  the created VComponent with {@link RequestStatus REQUEST-STATUS} populated to indicate success or failuer.
     */
    @Deprecated // replace with addVCalendar
    public VComponent importVComponent(String contentText)
    {
//        VPersonal vComponent = (VPersonal) SimpleVComponentFactory.emptyVComponent(contentText);
        VPersonal vComponent = null; //TODO - FIX THIS
        List contentLines = Arrays.asList(contentText.split(System.lineSeparator()));
        UnfoldingStringIterator unfoldedLines = new UnfoldingStringIterator(contentLines.iterator());
//        Iterator unfoldedLines = ICalendarUtilities.unfoldLines(contentLines).iterator();
        boolean useRequestStatus = true;
//        vComponent.parseContent(unfoldedLines, useRequestStatus);
        vComponent.parseContent(unfoldedLines);
//        requestStatusErrors.stream().forEach(System.out::println);
        // TODO - only check conflict if opaque
        String conflict = (vComponent instanceof VEvent) ? DateTimeUtilities.checkScheduleConflict((VEvent) vComponent, getVEvents()) : null;
        if (conflict != null)
        {
            final List requestStatus;
            if (vComponent.getRequestStatus() == null)
            {
                requestStatus = new ArrayList<>();
                vComponent.setRequestStatus(requestStatus);
            } else
            {
                requestStatus = vComponent.getRequestStatus();
            }
            // remove success REQUEST-STATUS message, if present
            Iterator rsIterator = requestStatus.iterator();
            while (rsIterator.hasNext())
            {
                RequestStatus rs = rsIterator.next();
                if (rs.getValue().substring(0, 3).equals("2.0"))
                {
                    rsIterator.remove();
                }
            }
            requestStatus.add(RequestStatus.parse("4.1;Event conflict with " + conflict));
        }
        
        final boolean isVComponentValidToAdd;
        if (vComponent.getRequestStatus() == null)
        {
            isVComponentValidToAdd = true;
        } else
        {
            isVComponentValidToAdd = ! vComponent.getRequestStatus().stream()
                    .map(s -> s.getValue())
                    .anyMatch(s -> (s.charAt(0) == '3') || (s.charAt(0) == '4')); // error codes start with 3 or 4
        }
        
        if (isVComponentValidToAdd)
        {
            addChild(vComponent);
        }
        return vComponent;
    }
     
    /**
     * Import new VComponent with {@link RequestStatus REQUEST-STATUS} properties containing 
     * the result of the process, such as success message or error report.
     * 
     * @param contentText  iCalendar content lines
     * @return  list of error messages if import failed, null if successful
     */
    @Deprecated
    public List importVComponent(VComponent newVComponent)
    {
        throw new RuntimeException("not implemented");
    }
    
    @Override
    public List errors()
    {
        List errors = super.errors();
        if (getProductIdentifier() == null)
        {
            errors.add("PRODID is not present.  PRODID is REQUIRED and MUST NOT occur more than once");
        }
        if (getVersion() == null)
        {
            errors.add("VERSION is not present.  VERSION is REQUIRED and MUST NOT occur more than once");
        }
        return errors;
    }

    
    /*
     * CONSTRUCTORS
     */
    /** Creates an empty VCalendar */
    public VCalendar()
    {
        setMethodProcessFactory(new DefaultITIPFactory());
//    	List getters = ICalendarUtilities.collectGetters(getClass());
        orderer = new OrdererBase(this, getGetters());
        contentLineGenerator = new MultiLineContent(
                orderer,
                FIRST_CONTENT_LINE,
                LAST_CONTENT_LINE,
                1000);
    }
  
    /** Copy constructor */
    public VCalendar(VCalendar source)
    {
        this();
        source.copyChildrenInto(this);  
    }

    /*
     * OTHER METHODS
     */
        
//    @Override
//    public List parseContent(String content)
//    {
//        Iterator lineIterator = Arrays.asList(content.split(System.lineSeparator())).iterator();
//        return parseContent(lineIterator)
//                .entrySet()
//                .stream()
//                .flatMap(e -> e.getValue().stream().map(v -> e.getKey().name() + ":" + v))
//                .collect(Collectors.toList());
//    }

    /** Parse unfolded content line iterator into calendar object */
////    @Override
//	public Map> parseContent(Iterator unfoldedLineIterator)
//    {
//        boolean useResourceStatus = false;
//        return parseContent(unfoldedLineIterator, useResourceStatus);
//    }
    
//    /** Parse unfolded content lines into calendar object */
//    public List parseContent(Iterator lineIterator, boolean collectErrorMessages)
//    {
//        List vCalendarMessages = new ArrayList<>();
//        List messages = new ArrayList<>();
//        String firstLine = lineIterator.next();
//        if (! firstLine.equals("BEGIN:VCALENDAR"))
//        {
//            throw new IllegalArgumentException("Content lines must begin with BEGIN:VCALENDAR");
//        }
//        // wrap lineIterator in UnfoldingStringIterator decorator
////        int line = 0;
//        UnfoldingStringIterator unfoldedLineIterator = new UnfoldingStringIterator(lineIterator);
//        while (unfoldedLineIterator.hasNext())
//        {
//            String unfoldedLine = unfoldedLineIterator.next();
////            System.out.println("unfoldedLine:" + unfoldedLine);
////            if (line++ > 20000) System.exit(0);
//            int nameEndIndex = ICalendarUtilities.getPropertyNameIndex(unfoldedLine);
//            String propertyName = (nameEndIndex > 0) ? unfoldedLine.substring(0, nameEndIndex) : "";
//            
//            // Parse component
//            if (propertyName.equals("BEGIN"))
//            {
//                String componentName = unfoldedLine.substring(nameEndIndex+1);
////            	System.out.println(componentName);
//                VComponentBase newComponent = (VComponentBase) SimpleVComponentFactory.emptyVComponent(componentName);
////                Map> newComponentMessages = newComponent.parseContent(unfoldedLineIterator, collectErrorMessages);
//                List newComponentMessages = newComponent.parseContent(unfoldedLineIterator);
//                // TODO - USE ELEMENTS STATICS
//                addChild(newComponent);
////                addVComponent(newComponent);
////                System.out.println(childrenUnmodifiable().size());
//                messages.addAll(newComponentMessages);
//
//                
////                String componentName = unfoldedLine.substring(nameEndIndex+1);
////                VComponent newComponent = SimpleVComponentFactory.emptyVComponent(componentName);
////                // TODO - USE ADD CHILD
////                System.out.println("new component");
////                Map> newComponentMessages = newComponent.parseContent(unfoldedLineIterator, collectErrorMessages);
//////                addChild(newComponent);
////                addVComponent(newComponent);
////                messageMap.putAll(newComponentMessages);
//            } else if (propertyName.equals("END"))
//            {
//                break;
//            } else
//            { // parse calendar property
//                VChild child = null;
//                CalendarProperty elementType = CalendarProperty.enumFromName(propertyName);
//                if (elementType != null)
//                {
//                    child = elementType.parse(this, unfoldedLine);
//                } else if (unfoldedLine.contains(":"))
//                {
//                    //non-standard - check for X- prefix
//                    boolean isNonStandard = propertyName.substring(0, PropertyType.NON_STANDARD.toString().length()).equals(PropertyType.NON_STANDARD.toString());
//                    if (isNonStandard)
//                    {
//                    	child = NonStandardProperty.parse(unfoldedLine);
//                    	addChild(child);
//                    } else
//                    {
//                        // ignore unknown properties
//                        vCalendarMessages.add("Unknown property is ignored:" + unfoldedLine);
//                    }
//                } else
//                {
//                    vCalendarMessages.add("Unknown line is ignored:" + unfoldedLine);                    
//                }
//                if (child != null) 
//            	{
//                	vCalendarMessages.addAll(child.errors());
////                	addChild(child); // TODO - USE WHEN NO USING ENUM PARSE ANYMORE.
//            	}
//            }
//        }
//     // TODO - Log status messages if not using RequestStatus
////        messageMap.put(this, vCalendarMessages);
//        return messages;
//    }

//    // multi threaded
//    /** Parse content lines into calendar object */
//    // TODO - TEST THIS - MAY NOT MAINTAIN ORDER
//    // TODO - FIX THIS - DOESN'T WORK, DOESN'T GET CALENDARY PROPERTIES
//    public void parseContentMulti(Iterator lineIterator)
//    {
//        // Callables to generate components
//        ExecutorService service = Executors.newWorkStealingPool();
////        Map> taskMap = new LinkedHashMap<>();
//        Integer order = 0;
//        List> tasks = new ArrayList<>();
//        
//        String firstLine = lineIterator.next();
//        if (! firstLine.equals("BEGIN:VCALENDAR"))
//        {
//            throw new IllegalArgumentException("Content lines must begin with BEGIN:VCALENDAR");
//        }
//        while (lineIterator.hasNext())
//        {
//            String line = lineIterator.next();
//            int nameEndIndex = ICalendarUtilities.getPropertyNameIndex(line);
//            String propertyName = line.substring(0, nameEndIndex);
//            
//            // Parse component
//            if (propertyName.equals("BEGIN"))
//            {
//                String componentName = line.substring(nameEndIndex+1);
//                List myLines = new ArrayList<>(20);
//                myLines.add(line);
//                final String endLine = "END:" + componentName;
//                while (lineIterator.hasNext())
//                {
//                    String myLine = lineIterator.next();
//                    myLines.add(myLine);
//                    if (myLine.equals(endLine))
//                    {
//                        Integer myOrder = order;
//                        order += 100;
//                        Runnable vComponentRunnable = () -> 
//                        {
//                            CalendarComponent elementType = CalendarComponent.valueOf(componentName);
//                            VElement component = elementType.parse(this, myLines);
////                            orderer().elementSortOrderMap().put((VChild) component, myOrder);
//                        };
////                        taskMap.put(order, Executors.callable(vComponentRunnable));
//                        tasks.add(Executors.callable(vComponentRunnable));
//                        break;
//                    }
//                }
//                
//            // parse calendar properties (ignores unknown properties)
//            } else
//            {
//                CalendarComponent elementType = CalendarComponent.enumFromName(propertyName);
//                if (elementType != null)
//                {
//                    VElement property = elementType.parse(this, Arrays.asList(line));
////                    orderer().elementSortOrderMap().put((VChild) property, order);
////                    order += 100;
//                }
//            }
//        }
//        
//        try
//        {
////            List> tasks = taskMap.entrySet().stream().map(e -> e.getValue()).collect(Collectors.toList());
//            service.invokeAll(tasks);
//        } catch (InterruptedException e)
//        {
//            e.printStackTrace();
//        }
//    }
    
    public static VCalendar parse(Reader reader) throws IOException
    {
        BufferedReader br = new BufferedReader(reader);
        Iterator unfoldedLineIterator = new UnfoldingStringIterator(br.lines().iterator());
//        Iterator unfoldedLineIterator = br.lines().iterator();
//        UnfoldingBufferedReader unfoldingReader = new UnfoldingBufferedReader(reader);
//        Iterator unfoldedLineIterator = unfoldingReader.lines().iterator();
        VCalendar vCalendar = new VCalendar();
        vCalendar.parseContent(unfoldedLineIterator);
//        unfoldingReader.close();
        return vCalendar;
    }
    
    /**
     * Creates a new VCalendar from an ics file
     * 
     * @param icsFilePath  path of ics file to parse
     * @return  Created VCalendar
     * @throws IOException
     */
    public static VCalendar parse(Path icsFilePath) throws IOException
    {
        return parse(Files.newBufferedReader(icsFilePath));
    }
    
    /**
     * Creates a new VCalendar from an ics file
     * 
     * @param icsFilePath  path of ics file to parse
     * @return  Created VCalendar
     * @throws IOException
     */
    // TODO - REMOVE useResourceStatus
    public static VCalendar parseICalendarFile(Path icsFilePath, boolean useResourceStatus) throws IOException
    {
        BufferedReader br = Files.newBufferedReader(icsFilePath);
        List lines = br.lines().collect(Collectors.toList());
//        Iterator unfoldedLines = ICalendarUtilities.unfoldLines(lines).iterator();
        VCalendar vCalendar = new VCalendar();
//        vCalendar.parseContent(lines.iterator(), useResourceStatus);
        vCalendar.parseContent(lines.iterator());
        return vCalendar;
    }
    
    /**
     * Creates a new VCalendar from an ics file
     * 
     * @param icsFilePath  path of ics file to parse
     * @return  Created VCalendar
     * @throws IOException
     */
    // TODO - REMOVE useResourceStatus
    public static VCalendar parseICalendarFile(Path icsFilePath) throws IOException
    {
        BufferedReader br = Files.newBufferedReader(icsFilePath);
        List lines = br.lines().collect(Collectors.toList());
//        Iterator unfoldedLines = ICalendarUtilities.unfoldLines(lines).iterator();
        VCalendar vCalendar = new VCalendar();
        vCalendar.parseContent(lines.iterator());
        return vCalendar;
    }
    
	@Override
	protected boolean isContentValid(String valueContent)
	{
		boolean isElementValid = super.isContentValid(valueContent);
		if (! isElementValid) return false;
		boolean isBeginPresent = valueContent.startsWith(FIRST_CONTENT_LINE);
		if (! isBeginPresent) return false;
		int lastLineIndex = valueContent.lastIndexOf(System.lineSeparator());
		if (lastLineIndex == -1) return false;
		boolean isEndPresent = valueContent
				.substring(lastLineIndex)
				.equals(LAST_CONTENT_LINE);
		return ! isEndPresent;
	}

    /**
     * Creates a new VCalendar calendar component by parsing a String of iCalendar content lines
     *
     * @param content  the text to parse, not null
     * @return  the parsed VCalendar
     */
    public static VCalendar parse(String content)
    {
    	return VCalendar.parse(new VCalendar(), content);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy