File : data/Collection.js

1
/*
2
Copyright - 2017 2023 - wwwouaiebe - Contact: https://www.ouaie.be/
3
4
This  program is free software;
5
you can redistribute it and/or modify it under the terms of the
6
GNU General Public License as published by the Free Software Foundation;
7
either version 3 of the License, or any later version.
8
This program is distributed in the hope that it will be useful,
9
but WITHOUT ANY WARRANTY; without even the implied warranty of
10
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
GNU General Public License for more details.
12
You should have received a copy of the GNU General Public License
13
along with this program; if not, write to the Free Software
14
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
15
*/
16
/*
17
Changes:
18
    - v4.0.0:
19
        - created from v3.6.0
20
Doc reviewed 202208
21
 */
22
23
import { ZERO, ONE, NEXT, PREVIOUS, TWO, NOT_FOUND } from '../main/Constants.js';
24
import CollectionIterator from './CollectionIterator.js';
25
26
/* ------------------------------------------------------------------------------------------------------------------------- */
27
/**
28
Class used to store objects in an iterable
29
*/
30
/* ------------------------------------------------------------------------------------------------------------------------- */
31
32
class Collection {
33
34
    /**
35
    The array where objects are stored
36
    @type {Array.<Object>}
37
    */
38
39
    #array;
40
41
    /**
42
    The class name of objects stored in the collection
43
    @type {String}
44
    */
45
46
    #objName;
47
48
    /**
49
    The class definition of objects stored in the collection
50
    @type {Class}
51
    */
52
53
    #classCollection;
54
55
    /**
56
    Return the position of an object in the Collection
57
    @param {Number} objId The objId of the object to locate
58
    @return {Number} the position of the object in the Collection
59
    */
60
61
    #indexOfObjId ( objId ) {
62
        return this.#array.findIndex (
63
            element => element.objId === objId
64
        );
65
    }
66
67
    /**
68
    Gives the previous or next object in the collection that fullfil a given condition
69
    @param {Number} objId The objId of the object from witch the search start
70
    @param {?function} condition A fonction used to compare the objects. If null, ( ) => true is used
71
    @param {Number} direction The direction to follow. Must be NEXT or PREVIOUS
72
    @return {?Object} An object or null if nothing found
73
    */
74
75
    #nextOrPrevious ( objId, condition, direction ) {
76
        let index = this.#indexOfObjId ( objId );
77
        if ( NOT_FOUND === index ) {
78
            throw new Error ( 'invalid objId for next or previous function' );
79
        }
80
        if ( direction !== NEXT && direction !== PREVIOUS ) {
81
            throw new Error ( 'invalid direction' );
82
        }
83
84
        let otherCondition = condition;
85
        if ( ! otherCondition ) {
86
            otherCondition = ( ) => true;
87
        }
88
        index += direction;
89
90
        while ( ( NOT_FOUND < index ) && ( index < this.#array.length ) && ! otherCondition ( this.#array [ index ] ) ) {
91
            index += direction;
92
        }
93
        if ( NOT_FOUND === index || this.#array.length === index ) {
94
            return null;
95
        }
96
97
        return this.#array [ index ];
98
    }
99
100
    /**
101
    The constructor
102
    @param {class} classCollection The class of objects that have to be stored in the collection
103
    */
104
105
    constructor ( classCollection ) {
106
        Object.freeze ( this );
107
        this.#array = [];
108
        this.#classCollection = classCollection;
109
        const tmpObject = new classCollection ( );
110
        if ( ( ! tmpObject.objType ) || ( ! tmpObject.objType.name ) ) {
111
            throw new Error ( 'invalid object name for collection' );
112
        }
113
        this.#objName = tmpObject.objType.name;
114
    }
115
116
    /**
117
    Add an object at the end of the collection
118
    @param {TravelObject} object The object to add
119
    */
120
121
    add ( object ) {
122
        if ( ( ! object.objType ) || ( ! object.objType.name ) || ( object.objType.name !== this.#objName ) ) {
123
            throw new Error ( 'invalid object name for add function' );
124
        }
125
        this.#array.push ( object );
126
    }
127
128
    /**
129
    Search an object in the collection with the index
130
    @param {Number} index The position of the desired object in the array
131
    @return {?Object} The object at the position or null if not found
132
    */
133
134
    at ( index ) {
135
        return ( index < this.#array.length && index > NOT_FOUND ) ? this.#array [ index ] : null;
136
    }
137
138
    /**
139
    Executes a function on each object of the Collection and returns the final result
140
    @param {function} funct The function to execute
141
    */
142
143
    forEach ( funct ) {
144
        let result = null;
145
        const iterator = this.iterator;
146
        while ( ! iterator.done ) {
147
            result = funct ( iterator.value, result );
148
        }
149
        return result;
150
    }
151
152
    /**
153
    Search an object in the Collection
154
    @param {Number} objId The objId of the object to search
155
    @return {TravelObject} the object with the given objId or null when the object is not found
156
    */
157
158
    getAt ( objId ) {
159
        const index = this.#indexOfObjId ( objId );
160
        return NOT_FOUND === index ? null : this.#array [ index ];
161
    }
162
163
    /**
164
    Move an object near another object in the Collection
165
    @param {Number} objId The objId of the object to move
166
    @param {Number} targetObjId The objId of the object near witch the object will be moved
167
    @param {Boolean} moveBefore When true, the object is moved before the target, when false after the target
168
    */
169
170
    moveTo ( objId, targetObjId, moveBefore ) {
171
        let oldPosition = this.#indexOfObjId ( objId );
172
        let newPosition = this.#indexOfObjId ( targetObjId );
173
        if ( NOT_FOUND === oldPosition || NOT_FOUND === newPosition ) {
174
            throw new Error ( 'invalid objId for function  myMoveTo' );
175
        }
176
        if ( ! moveBefore ) {
177
            newPosition ++;
178
        }
179
        this.#array.splice ( newPosition, ZERO, this.#array [ oldPosition ] );
180
        if ( newPosition < oldPosition ) {
181
            oldPosition ++;
182
        }
183
        this.#array.splice ( oldPosition, ONE );
184
    }
185
186
    /**
187
    gives the next object in the collection that fullfil a given condition
188
    @param {Number} objId The objId of the object from witch the search start
189
    @param {?function} condition A fonction used to compare the objects. If null, ( ) => true is used
190
    @return {?Object} An object or null if nothing found
191
    */
192
193
    next ( objId, condition ) { return this.#nextOrPrevious ( objId, condition, NEXT ); }
194
195
    /**
196
    gives the previous object in the collection that fullfil a given condition
197
    @param {Number} objId The objId of the object from witch the search start
198
    @param {?function} condition A fonction used to compare the objects. If null, ( ) => true is used
199
    @return {?Object} An object or null if nothing found
200
    */
201
202
    previous ( objId, condition ) { return this.#nextOrPrevious ( objId, condition, PREVIOUS ); }
203
204
    /**
205
    Remove an object from the Collection
206
    @param {Number} objId The objId of the object to remove
207
    */
208
209
    remove ( objId ) {
210
        const index = this.#indexOfObjId ( objId );
211
        if ( NOT_FOUND === index ) {
212
            throw new Error ( 'invalid objId for remove function' );
213
        }
214
        this.#array.splice ( index, ONE );
215
    }
216
217
    /**
218
    Remove all objects from the Collection
219
    @param {?boolean} exceptFirstLast When true, first and last objects are not removed
220
    */
221
222
    removeAll ( exceptFirstLast ) {
223
        if ( exceptFirstLast ) {
224
            this.#array.splice ( ONE, this.#array.length - TWO );
225
        }
226
        else {
227
            this.#array.length = ZERO;
228
        }
229
    }
230
231
    /**
232
    Replace an object in the Collection with another object
233
    @param {Number} oldObjId the objId of the object to replace
234
    @param {TravelObject} newObject The new object
235
    */
236
237
    replace ( oldObjId, newObject ) {
238
        const index = this.#indexOfObjId ( oldObjId );
239
        if ( NOT_FOUND === index ) {
240
            throw new Error ( 'invalid objId for replace function' );
241
        }
242
        if ( ( ! newObject.objType ) || ( ! newObject.objType.name ) || ( newObject.objType.name !== this.#objName ) ) {
243
            throw new Error ( 'invalid object name for replace function' );
244
        }
245
        this.#array [ index ] = newObject;
246
    }
247
248
    /**
249
    Reverse the objects in the collection
250
    */
251
252
    reverse ( ) { this.#array.reverse ( ); }
253
254
    /**
255
    Sort the collection, using a function
256
    @param {function} compareFunction The function to use to compare objects in the Collection
257
    */
258
259
    sort ( compareFunction ) { this.#array.sort ( compareFunction ); }
260
261
    /**
262
    Reverse an Object with the previous or next object in the Collection
263
    @param {Number} objId The objId of the object to swap
264
    @param {Boolean} swapUp When true the object is swapped with the previous one,
265
    when false with the next one
266
    */
267
268
    swap ( objId, swapUp ) {
269
        const index = this.#indexOfObjId ( objId );
270
        if (
271
            ( NOT_FOUND === index )
272
            ||
273
            ( ( ZERO === index ) && swapUp )
274
            ||
275
            ( ( this.#array.length - ONE === index ) && ( ! swapUp ) )
276
        ) {
277
            throw new Error ( 'invalid objId for swap function' );
278
        }
279
        const swap = swapUp ? PREVIOUS : NEXT;
280
        const tmp = this.#array [ index ];
281
        this.#array [ index ] = this.#array [ index + swap ];
282
        this.#array [ index + swap ] = tmp;
283
    }
284
285
    /**
286
    The first object of the Collection
287
    @type {TravelObject}
288
    */
289
290
    get first ( ) { return this.#array [ ZERO ]; }
291
292
    /**
293
    An iterator on the Collection. See CollectionIterator
294
    @type {CollectionIterator}
295
296
    */
297
298
    get iterator ( ) {
299
        return new CollectionIterator ( this );
300
    }
301
302
    /**
303
    The last object of the Collection
304
    @type {TravelObject}
305
    */
306
307
    get last ( ) { return this.#array [ this.#array.length - ONE ]; }
308
309
    /**
310
    The length of the Collection
311
    @type {Number}
312
    */
313
314
    get length ( ) { return this.#array.length; }
315
316
    /**
317
    an Array with the objects in the collection
318
    @type {Array}
319
    */
320
321
    get jsonObject ( ) {
322
        const array = [ ];
323
        const iterator = this.iterator;
324
        while ( ! iterator.done ) {
325
            array.push ( iterator.value.jsonObject );
326
        }
327
328
        return array;
329
    }
330
331
    set jsonObject ( something ) {
332
        this.#array.length = ZERO;
333
334
        if ( ! Array.isArray ( something ) ) {
335
            return;
336
        }
337
338
        something.forEach (
339
            arrayObject => {
340
                const newObject = new this.#classCollection ( );
341
                newObject.jsonObject = arrayObject;
342
                this.add ( newObject );
343
            }
344
        );
345
    }
346
}
347
348
export default Collection;
349
350
/* --- End of file --------------------------------------------------------------------------------------------------------- */
351