9
9
import java .util .Collections ;
10
10
import java .util .HashSet ;
11
11
import java .util .Iterator ;
12
- import java .util .LinkedHashMap ;
13
12
import java .util .List ;
14
- import java .util .Map ;
15
13
import java .util .NavigableMap ;
16
14
import java .util .NavigableSet ;
15
+ import java .util .NoSuchElementException ;
17
16
import java .util .Objects ;
18
17
import java .util .Optional ;
19
- import java .util .Set ;
20
18
import java .util .TreeMap ;
21
19
import java .util .TreeSet ;
22
20
import java .util .concurrent .atomic .AtomicReference ;
@@ -204,24 +202,18 @@ public <T, R> LocalDateTimeline<R> combine(final LocalDateSegment<T> other,
204
202
* <p>
205
203
* beholder inntil videre for å kunne teste ny mot gammel implementasjon
206
204
*/
207
- @ Deprecated
208
205
<T , R > LocalDateTimeline <R > combineGammel (final LocalDateTimeline <T > other , final LocalDateSegmentCombinator <V , T , R > combinator ,
209
206
final JoinStyle combinationStyle ) {
210
207
211
- LocalDateTimeline <R > quickExit = combinationStyle .checkQuickExit (this , other );
212
- if (quickExit != null ) {
213
- return quickExit ;
214
- }
215
-
216
208
// Join alle intervaller
217
- final NavigableMap <LocalDateInterval , Integer > joinDatoInterval = joinLocalDateIntervals (getDatoIntervaller (),
218
- other .getDatoIntervaller ());
209
+ final NavigableMap <LocalDateInterval , Integer > joinDatoInterval = joinLocalDateIntervals (getLocalDateIntervals (),
210
+ other .getLocalDateIntervals ());
219
211
220
212
// filtrer ut i henhold til combinationStyle
221
213
final List <LocalDateSegment <R >> combinedSegmenter = new ArrayList <>();
222
214
final LocalDateTimeline <V > myTidslinje = this ;
223
215
joinDatoInterval .entrySet ().stream ()
224
- .filter (e -> combinationStyle .accept (e .getValue ()))
216
+ .filter (e -> combinationStyle .accept (( e .getValue () & LHS ) > 0 , ( e . getValue () & RHS ) > 0 ))
225
217
.forEachOrdered (e -> {
226
218
LocalDateInterval key = e .getKey ();
227
219
LocalDateSegment <R > nyVerdi = combinator .combine (key , myTidslinje .getSegment (key ),
@@ -238,29 +230,46 @@ <T, R> LocalDateTimeline<R> combineGammel(final LocalDateTimeline<T> other, fina
238
230
* Kombinerer to tidslinjer, med angitt combinator funksjon og {@link JoinStyle}.
239
231
*/
240
232
public <T , R > LocalDateTimeline <R > combine (final LocalDateTimeline <T > other , final LocalDateSegmentCombinator <V , T , R > combinator , final JoinStyle combinationStyle ) {
241
-
242
- LocalDateTimeline <R > quickExit = combinationStyle .checkQuickExit (this , other );
243
- if (quickExit != null ) {
244
- return quickExit ;
233
+ List <LocalDateSegment <R >> combinedSegmenter = new ArrayList <>();
234
+ Iterator <LocalDateSegment <V >> lhsIterator = this .segments .iterator ();
235
+ Iterator <LocalDateSegment <T >> rhsIterator = other .segments .iterator ();
236
+ LocalDateSegment <V > lhs = lhsIterator .hasNext () ? lhsIterator .next () : null ;
237
+ LocalDateSegment <T > rhs = rhsIterator .hasNext () ? rhsIterator .next () : null ;
238
+ Iterator <LocalDate > startdatoIterator = new KnekkpunktIterator <>(this .segments , other .segments );
239
+ if (!startdatoIterator .hasNext ()) {
240
+ return empty (); //begge input-tidslinjer var tomme
245
241
}
246
242
247
- NavigableSet <LocalDateInterval > intervallerIKombinertTidslinje = utedIntervallerIKombinertTidslinje (toSegments (), other .toSegments (), combinationStyle );
248
-
249
- //henter alle verdier med en gang, slipper å iterere i hver tidslinje en gang pr intervall
250
- Map <LocalDateInterval , LocalDateSegment <V >> aktuelleVerdierFraDenneTidslinje = this .getSegments (intervallerIKombinertTidslinje );
251
- Map <LocalDateInterval , LocalDateSegment <T >> aktuelleVerdierFraAndreTidslinje = other .getSegments (intervallerIKombinertTidslinje );
243
+ LocalDate fom = startdatoIterator .next ();
244
+ while (startdatoIterator .hasNext ()) {
245
+ lhs = spolTil (lhs , lhsIterator , fom );
246
+ rhs = spolTil (rhs , rhsIterator , fom );
252
247
253
- List <LocalDateSegment <R >> combinedSegmenter = new ArrayList <>();
254
- intervallerIKombinertTidslinje .stream ().forEachOrdered (key -> {
255
- LocalDateSegment <R > nyVerdi = combinator .combine (key , aktuelleVerdierFraDenneTidslinje .get (key ), aktuelleVerdierFraAndreTidslinje .get (key ));
256
- if (nyVerdi != null ) {
257
- combinedSegmenter .add (nyVerdi );
248
+ boolean harLhs = lhs != null && lhs .getLocalDateInterval ().contains (fom );
249
+ boolean harRhs = rhs != null && rhs .getLocalDateInterval ().contains (fom );
250
+ LocalDate nesteFom = startdatoIterator .next ();
251
+ if (combinationStyle .accept (harLhs , harRhs )) {
252
+ LocalDateInterval periode = new LocalDateInterval (fom , nesteFom .minusDays (1 ));
253
+ LocalDateSegment <V > tilpassetLhsSegment = harLhs ? splittVedDelvisOverlapp (this .segmentSplitter , lhs , periode ) : null ;
254
+ LocalDateSegment <T > tilpassetRhsSegment = harRhs ? splittVedDelvisOverlapp (other .segmentSplitter , rhs , periode ) : null ;
255
+ LocalDateSegment <R > nyVerdi = combinator .combine (periode , tilpassetLhsSegment , tilpassetRhsSegment );
256
+ if (nyVerdi != null ) {
257
+ combinedSegmenter .add (nyVerdi );
258
+ }
258
259
}
259
- }) ;
260
-
260
+ fom = nesteFom ;
261
+ }
261
262
return new LocalDateTimeline <>(combinedSegmenter );
262
263
}
263
264
265
+ private static <X > LocalDateSegment <X > splittVedDelvisOverlapp (SegmentSplitter <X > segmentSplitter , LocalDateSegment <X > segment , LocalDateInterval ønsketIntervall ) {
266
+ if (segment == null || segment .getLocalDateInterval ().equals (ønsketIntervall )) {
267
+ return segment ;
268
+ }
269
+ return segmentSplitter .apply (ønsketIntervall , segment );
270
+ }
271
+
272
+
264
273
/**
265
274
* Fikser opp tidslinjen slik at tilgrensende intervaller med equal verdi får et sammenhengende intervall. Nyttig
266
275
* for å 'redusere' tidslinjen ned til enkleste form før lagring etc.
@@ -410,31 +419,6 @@ public LocalDateSegment<V> getSegment(LocalDateInterval datoInterval) {
410
419
}
411
420
}
412
421
413
- private Map <LocalDateInterval , LocalDateSegment <V >> getSegments (NavigableSet <LocalDateInterval > datoInterval ) {
414
- if (isEmpty () || datoInterval .isEmpty ()) {
415
- return Map .of ();
416
- }
417
-
418
- Iterator <LocalDateSegment <V >> segmentIterator = segments .iterator ();
419
- LocalDateSegment <V > segment = segmentIterator .next ();
420
-
421
- Map <LocalDateInterval , LocalDateSegment <V >> resultat = new LinkedHashMap <>();
422
- for (LocalDateInterval datoIntervall : datoInterval ) {
423
- while (segment != null && segment .getTom ().isBefore (datoIntervall .getFomDato ())) {
424
- segment = segmentIterator .hasNext () ? segmentIterator .next () : null ;
425
- }
426
- if (segment != null && segment .getLocalDateInterval ().overlaps (datoIntervall )) {
427
- if (segment .getLocalDateInterval ().equals (datoIntervall )) {
428
- resultat .put (datoIntervall , segment );
429
- } else {
430
- resultat .put (datoIntervall , segmentSplitter .apply (datoIntervall , segment ));
431
- }
432
- }
433
- }
434
- return resultat ;
435
- }
436
-
437
-
438
422
@ Override
439
423
public int hashCode () {
440
424
return segments .hashCode ();
@@ -610,7 +594,7 @@ public String toString() {
610
594
(isEmpty () ? "0" //$NON-NLS-1$
611
595
: getMinLocalDate () + ", " + getMaxLocalDate ()) //$NON-NLS-1$
612
596
+ " [" + size () + "]" //$NON-NLS-1$ // $NON-NLS-2$
613
- + "> = [" + getDatoIntervaller ().stream ().map (String ::valueOf ).collect (Collectors .joining ("," )) + "]" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
597
+ + "> = [" + getLocalDateIntervals ().stream ().map (String ::valueOf ).collect (Collectors .joining ("," )) + "]" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
614
598
;
615
599
}
616
600
@@ -796,53 +780,74 @@ private NavigableMap<LocalDateInterval, Integer> joinLocalDateIntervals(Navigabl
796
780
return joined ;
797
781
}
798
782
799
- private <T > NavigableSet <LocalDateInterval > utedIntervallerIKombinertTidslinje (NavigableSet <LocalDateSegment <V >> lhsIntervaller , NavigableSet <LocalDateSegment <T >> rhsIntervaller , JoinStyle combinationStyle ) {
800
- if (lhsIntervaller .isEmpty ()) {
801
- return combinationStyle .accept (RHS ) ? toLocalDateIntervals (rhsIntervaller ) : new TreeSet <>();
802
- }
803
- if (rhsIntervaller .isEmpty ()) {
804
- return combinationStyle .accept (LHS ) ? toLocalDateIntervals (lhsIntervaller ) : new TreeSet <>();
783
+ /**
784
+ * Finner alle knekkpunkter fra to tidslinjer, i sekvens.
785
+ * <p>
786
+ * Knekkpunkter er 'start av et intervall' og 'dagen etter slutt av et intervall'. Sistnevnte fordi det da kan være starten på et nytt intervall
787
+ * <p>
788
+ * Tidliger implementert ved å dytte alle knekkpunkter i et TreeSet og iterere, men dette er raskere O(n) vs O(n ln n), og bruker mindre minne
789
+ */
790
+ private static class KnekkpunktIterator <V , T > implements Iterator <LocalDate > {
791
+ private final Iterator <LocalDateSegment <V >> lhsIterator ;
792
+ private final Iterator <LocalDateSegment <T >> rhsIterator ;
793
+ private LocalDateSegment <V > lhsSegment ;
794
+ private LocalDateSegment <T > rhsSegment ;
795
+ private LocalDate next ;
796
+
797
+ public KnekkpunktIterator (NavigableSet <LocalDateSegment <V >> lhsIntervaller , NavigableSet <LocalDateSegment <T >> rhsIntervaller ) {
798
+ lhsIterator = lhsIntervaller .iterator ();
799
+ rhsIterator = rhsIntervaller .iterator ();
800
+ lhsSegment = lhsIterator .hasNext () ? lhsIterator .next () : null ;
801
+ rhsSegment = rhsIterator .hasNext () ? rhsIterator .next () : null ;
802
+
803
+ next = lhsSegment != null ? lhsSegment .getFom () : null ;
804
+ if (rhsSegment != null && (next == null || rhsSegment .getFom ().isBefore (next ))) {
805
+ next = rhsSegment .getFom ();
806
+ }
805
807
}
806
- NavigableSet <LocalDateInterval > joined = new TreeSet <>();
807
- Iterator <LocalDateSegment <V >> lhsIterator = lhsIntervaller .iterator ();
808
- Iterator <LocalDateSegment <T >> rhsIterator = rhsIntervaller .iterator ();
809
- LocalDateSegment <V > lhs = lhsIterator .next ();
810
- LocalDateSegment <T > rhs = rhsIterator .next ();
811
- Set <LocalDate > startdatoKandidater = finKnekkpunkter (lhsIntervaller , rhsIntervaller );
812
- Iterator <LocalDate > startdatoIterator = startdatoKandidater .iterator ();
813
- LocalDate fom = startdatoIterator .next ();
814
- while (startdatoIterator .hasNext ()) {
815
- lhs = spolTil (lhs , lhsIterator , fom );
816
- rhs = spolTil (rhs , rhsIterator , fom );
817
808
818
- boolean lhsMatch = lhs != null && lhs .getLocalDateInterval ().contains (fom );
819
- boolean rhsMatch = rhs != null && rhs .getLocalDateInterval ().contains (fom );
820
- int combinedFlags = (lhsMatch ? LHS : 0 ) | (rhsMatch ? RHS : 0 );
821
- LocalDate nesteFom = startdatoIterator .next ();
822
- if (combinedFlags > 0 && combinationStyle .accept (combinedFlags )) {
823
- joined .add (new LocalDateInterval (fom , nesteFom .minusDays (1 )));
809
+ @ Override
810
+ public LocalDate next () {
811
+ if (next == null ) {
812
+ throw new NoSuchElementException ("Ikke flere verdier igjen" );
824
813
}
825
- fom = nesteFom ;
814
+ LocalDate denne = next ;
815
+ oppdaterNeste ();
816
+ return denne ;
826
817
}
827
818
828
- return joined ;
829
- }
819
+ @ Override
820
+ public boolean hasNext () {
821
+ return next != null ;
822
+ }
830
823
831
- private static <T > NavigableSet <LocalDateInterval > toLocalDateIntervals (NavigableSet <LocalDateSegment <T >> rhsIntervaller ) {
832
- return rhsIntervaller .stream ().map (LocalDateSegment ::getLocalDateInterval ).collect (Collectors .toCollection (TreeSet ::new ));
833
- }
824
+ private void oppdaterNeste () {
825
+ while (lhsSegment != null && !lhsSegment .getTom ().plusDays (1 ).isAfter (next )) {
826
+ lhsSegment = lhsIterator .hasNext () ? lhsIterator .next () : null ;
827
+ }
828
+ while (rhsSegment != null && !rhsSegment .getTom ().plusDays (1 ).isAfter (next )) {
829
+ rhsSegment = rhsIterator .hasNext () ? rhsIterator .next () : null ;
830
+ }
834
831
835
- private static <V , T > Set <LocalDate > finKnekkpunkter (NavigableSet <LocalDateSegment <V >> lhsIntervaller , NavigableSet <LocalDateSegment <T >> rhsIntervaller ) {
836
- Set <LocalDate > startdatoKandidater = new TreeSet <>();
837
- for (LocalDateSegment <V > intervall : lhsIntervaller ) {
838
- startdatoKandidater .add (intervall .getFom ());
839
- startdatoKandidater .add (intervall .getTom ().plusDays (1 ));
832
+ LocalDate forrige = next ;
833
+ //neste knekkpunkt kan komme fra hvilken som helst av de to tidsseriene, må sjekke begge
834
+ next = oppdaterKandidatForNeste (forrige , null , lhsSegment );
835
+ next = oppdaterKandidatForNeste (forrige , next , rhsSegment );
840
836
}
841
- for (LocalDateSegment <T > intervall : rhsIntervaller ) {
842
- startdatoKandidater .add (intervall .getFom ());
843
- startdatoKandidater .add (intervall .getTom ().plusDays (1 ));
837
+
838
+ private static <X > LocalDate oppdaterKandidatForNeste (LocalDate forrige , LocalDate besteKandidat , LocalDateSegment <X > segment ) {
839
+ if (segment != null ) {
840
+ LocalDate fomKandidat = segment .getFom ();
841
+ if (fomKandidat .isAfter (forrige ) && (besteKandidat == null || fomKandidat .isBefore (besteKandidat ))) {
842
+ return fomKandidat ;
843
+ }
844
+ LocalDate tomKandidat = segment .getTom ().plusDays (1 );
845
+ if (tomKandidat .isAfter (forrige ) && (besteKandidat == null || tomKandidat .isBefore (besteKandidat ))) {
846
+ return tomKandidat ;
847
+ }
848
+ }
849
+ return besteKandidat ;
844
850
}
845
- return startdatoKandidater ;
846
851
}
847
852
848
853
private static <V > LocalDateSegment <V > spolTil (LocalDateSegment <V > intervall , Iterator <LocalDateSegment <V >> iterator , LocalDate fom ) {
@@ -873,49 +878,37 @@ public enum JoinStyle {
873
878
*/
874
879
CROSS_JOIN {
875
880
@ Override
876
- public boolean accept (int option ) {
877
- return option > 0 ;
881
+ public boolean accept (boolean harLhs , boolean harRhs ) {
882
+ return harLhs || harRhs ;
878
883
}
879
884
},
880
885
/**
881
886
* kun venstre tidsserie.
882
887
*/
883
888
DISJOINT {
884
889
@ Override
885
- public boolean accept (int option ) {
886
- return option == LHS ;
887
- }
888
-
889
- @ SuppressWarnings ("unchecked" )
890
- @ Override
891
- protected <V , T , R > LocalDateTimeline <R > checkQuickExit (LocalDateTimeline <V > lhs , LocalDateTimeline <T > rhs ) {
892
- return rhs .isEmpty () || lhs .isEmpty () ? (LocalDateTimeline <R >) lhs : null ;
890
+ public boolean accept (boolean harLhs , boolean harRhs ) {
891
+ return harLhs && !harRhs ;
893
892
}
894
893
},
895
894
/**
896
895
* kun dersom begge tidsserier har verdi.
897
896
*/
898
897
INNER_JOIN {
899
898
@ Override
900
- public boolean accept (int option ) {
901
- return ( option & LHS ) == LHS && ( option & RHS ) == RHS ;
899
+ public boolean accept (boolean harLhs , boolean harRhs ) {
900
+ return harLhs && harRhs ;
902
901
}
903
902
904
- @ Override
905
- protected <V , T , R > LocalDateTimeline <R > checkQuickExit (LocalDateTimeline <V > lhs , LocalDateTimeline <T > rhs ) {
906
- boolean skip = ((lhs .isEmpty () || rhs .isEmpty ()) || !new LocalDateInterval (lhs .getMinLocalDate (), lhs .getMaxLocalDate ())
907
- .overlaps (new LocalDateInterval (rhs .getMinLocalDate (), rhs .getMaxLocalDate ())));
908
- return skip ? new LocalDateTimeline <R >(Collections .emptyList ()) : null ;
909
- }
910
903
},
911
904
/**
912
905
* alltid venstre tidsserie (LHS), høyre (RHS) kun med verdi dersom matcher. Combinator funksjon må hensyn ta
913
906
* nulls for RHS.
914
907
*/
915
908
LEFT_JOIN {
916
909
@ Override
917
- public boolean accept (int option ) {
918
- return ( option & LHS ) == LHS ;
910
+ public boolean accept (boolean harLhs , boolean harRhs ) {
911
+ return harLhs ;
919
912
}
920
913
921
914
},
@@ -925,17 +918,13 @@ public boolean accept(int option) {
925
918
*/
926
919
RIGHT_JOIN {
927
920
@ Override
928
- public boolean accept (int option ) {
929
- return ( option & RHS ) == RHS ;
921
+ public boolean accept (boolean harLhs , boolean harRhs ) {
922
+ return harRhs ;
930
923
}
931
924
};
932
925
933
- public abstract boolean accept (int option );
926
+ public abstract boolean accept (boolean harLhs , boolean harRhs );
934
927
935
- @ SuppressWarnings ("unused" )
936
- protected <V , T , R > LocalDateTimeline <R > checkQuickExit (LocalDateTimeline <V > lhs , LocalDateTimeline <T > rhs ) {
937
- return null ;
938
- }
939
928
}
940
929
941
930
/**
0 commit comments