001/*
002 *  Copyright 2024 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.odf.data;
017
018import java.util.ArrayList;
019import java.util.Arrays;
020import java.util.List;
021import java.util.Objects;
022import java.util.stream.Collectors;
023import java.util.stream.Stream;
024
025import org.apache.commons.lang3.StringUtils;
026
027import org.ametys.odf.ProgramItem;
028import org.ametys.plugins.repository.AmetysObjectResolver;
029
030/**
031 * Class representing an educational path of a {@link ProgramItem}
032 */
033public class EducationalPath
034{
035    /** The program items identifier */
036    public static final String PATH_SEGMENTS_IDENTIFIER = "programItemIds";
037    
038    /** The separator for path segment */
039    public static final String PATH_SEGMENT_SEPARATOR = ";";
040    
041    private List<String> _programItemIds;
042    private List<ProgramItem> _programItems;
043
044    /**
045     * Constructs a EducationalPath
046     * @param programItemIds the ids of program items 
047     */
048    EducationalPath(List<String> programItemIds)
049    {
050        _programItemIds = programItemIds;
051    }
052    
053    /**
054     * Constructs a EducationalPath
055     * @param programItemsIds the ids of program items
056     */
057    EducationalPath(String[] programItemsIds)
058    {
059        _programItemIds = Arrays.asList(programItemsIds);
060    }
061    
062    /**
063     * Constructs a EducationalPath
064     * @param programItems the program items
065     */
066    EducationalPath(ProgramItem[] programItems)
067    {
068        _programItems = Arrays.asList(programItems);
069    }
070    
071    /**
072     * Build a educational path
073     * @param programItemIds the ids of program items
074     * @return the educational path
075     */
076    public static EducationalPath of(String... programItemIds)
077    {
078        return new EducationalPath(programItemIds);
079    }
080    
081    /**
082     * Build a educational path from a parent education path
083     * @param parentPath The parent educational path
084     * @param programItemIds the ids of program items
085     * @return the educational path
086     */
087    @SuppressWarnings("unchecked")
088    public static EducationalPath of(EducationalPath parentPath, String... programItemIds)
089    {
090        List<String> parentProgramItemIds = new ArrayList(parentPath.getProgramItemIds());
091        parentProgramItemIds.addAll(Arrays.asList(programItemIds));
092        return new EducationalPath(parentProgramItemIds);
093    }
094    
095    /**
096     * Build a educational path from a parent education path
097     * @param parentPath The parent educational path
098     * @param programItem the program items
099     * @return the educational path
100     */
101    public static EducationalPath of(EducationalPath parentPath, ProgramItem... programItem)
102    {
103        List<ProgramItem> parentProgramItems = parentPath._programItems;
104        if (parentProgramItems != null)
105        {
106            List<ProgramItem> programItems = new ArrayList<>(parentProgramItems);
107            programItems.addAll(Arrays.asList(programItem));
108            return new EducationalPath(programItems.toArray(ProgramItem[]::new));
109        }
110        else
111        {
112            List<String> parentProgramItemIds = new ArrayList<>(parentPath.getProgramItemIds());
113            parentProgramItemIds.addAll(Stream.of(programItem).map(ProgramItem::getId).collect(Collectors.toList()));
114            return new EducationalPath(parentProgramItemIds);
115        }
116    }
117    
118    /**
119     * Build a educational path
120     * @param programItems the program items
121     * @return the educational path
122     */
123    public static EducationalPath of(ProgramItem... programItems)
124    {
125        return new EducationalPath(programItems);
126    }
127    
128    /**
129     * Retrieves the program item ids that are the segment of this path
130     * @return the program item ids
131     */
132    public List<String> getProgramItemIds()
133    {
134        if (_programItemIds != null)
135        {
136            return _programItemIds;
137        }
138        else
139        {
140            return _programItems.stream().map(ProgramItem::getId).collect(Collectors.toList());
141        }
142    }
143    
144    /**
145     * Get {@link ProgramItem}s that composed this educational path as an unmodifiable list
146     * @param resolver the ametys object resolver
147     * @return the {@link ProgramItem}s of this path
148     */
149    public List<ProgramItem> getProgramItems(AmetysObjectResolver resolver)
150    {
151        if (_programItems != null)
152        {
153            return _programItems;
154        }
155        else
156        {
157            return resolveProgramItems(resolver).toList();
158        }
159    }
160    
161    /**
162     * Resolve {@link ProgramItem}s that composed this educational path 
163     * @param resolver the ametys object resolver
164     * @return the {@link ProgramItem}s of this path
165     */
166    public Stream<ProgramItem> resolveProgramItems(AmetysObjectResolver resolver)
167    {
168        if (_programItems != null)
169        {
170            return _programItems.stream();
171        }
172        else
173        {
174            return _programItemIds
175                    .stream()
176                    .map(id -> (ProgramItem) resolver.resolveById(id));
177        }
178    }
179    
180    @Override
181    public String toString()
182    {
183        return StringUtils.join(getProgramItemIds(), PATH_SEGMENT_SEPARATOR);
184    }
185    
186    @Override
187    public int hashCode()
188    {
189        return Objects.hash(getProgramItemIds());
190    }
191
192    @Override
193    public boolean equals(Object obj)
194    {
195        if (this == obj)
196        {
197            return true;
198        }
199        if (!(obj instanceof EducationalPath other))
200        {
201            return false;
202        }
203        
204        return Objects.equals(getProgramItemIds(), other.getProgramItemIds());
205    }
206}