22
33namespace Wikibase \DataModel \Services \Statement ;
44
5- use Wikibase \DataModel \ByPropertyIdArray ;
5+ use Wikibase \DataModel \Entity \ PropertyId ;
66use Wikibase \DataModel \Statement \Statement ;
77use Wikibase \DataModel \Statement \StatementList ;
88
@@ -20,18 +20,6 @@ public function clear( StatementList $statementList ) {
2020 }
2121 }
2222
23- /**
24- * @param StatementList $statementList
25- * @param Statement[] $statements
26- */
27- private function replaceStatements ( StatementList $ statementList , array $ statements ) {
28- $ this ->clear ( $ statementList );
29-
30- foreach ( $ statements as $ statement ) {
31- $ statementList ->addStatement ( $ statement );
32- }
33- }
34-
3523 public function groupByProperty ( StatementList $ statementList ) {
3624 $ byId = [];
3725
@@ -51,19 +39,116 @@ public function groupByProperty( StatementList $statementList ) {
5139
5240 /**
5341 * @param StatementList $statementList
54- * @param Statement $statement
42+ * @param Statement $newStatement
43+ * @param int|null $index An absolute index in the list. If the index is not next to a statement
44+ * with the same property ID, the closest possibly position is used instead. Default is null,
45+ * which adds the new statement after the last statement with the same property ID.
46+ */
47+ public function addToGroup (
48+ StatementList $ statementList ,
49+ Statement $ newStatement ,
50+ $ index = null
51+ ) {
52+ $ statements = $ statementList ->toArray ();
53+ $ id = $ newStatement ->getPropertyId ();
54+
55+ if ( $ index === null ) {
56+ $ index = $ this ->getLastIndexWithinGroup ( $ statements , $ id );
57+ } else {
58+ // Limit search range to avoid looping non-existing positions
59+ $ indexInRange = min ( max ( 0 , $ index ), count ( $ statements ) );
60+ $ index = $ this ->getClosestIndexWithinGroup ( $ statements , $ id , $ indexInRange );
61+ if ( $ index === null ) {
62+ $ index = $ this ->getClosestIndexAtGroupBorder ( $ statements , $ indexInRange );
63+ }
64+ }
65+
66+ $ statementList ->addStatement ( $ newStatement , $ index );
67+ }
68+
69+ /**
70+ * @param Statement[] $statements
71+ * @param PropertyId $id
72+ *
73+ * @return int|null
74+ */
75+ private function getLastIndexWithinGroup ( array $ statements , PropertyId $ id ) {
76+ for ( $ i = count ( $ statements ); $ i > 0 ; $ i -- ) {
77+ if ( $ statements [$ i - 1 ]->getPropertyId ()->equals ( $ id ) ) {
78+ return $ i ;
79+ }
80+ }
81+
82+ return null ;
83+ }
84+
85+ /**
86+ * @param Statement[] $statements
87+ * @param PropertyId $id
5588 * @param int $index
89+ *
90+ * @return int|null
5691 */
57- public function addToGroup ( StatementList $ statementList , Statement $ statement , $ index ) {
58- if ( $ statementList ->isEmpty () ) {
59- $ statementList ->addStatement ( $ statement );
60- return ;
92+ private function getClosestIndexWithinGroup ( array $ statements , PropertyId $ id , $ index ) {
93+ $ longestDistance = max ( $ index , count ( $ statements ) - $ index );
94+
95+ for ( $ i = 0 ; $ i <= $ longestDistance ; $ i ++ ) {
96+ if ( $ this ->isWithinGroup ( $ statements , $ id , $ index - $ i ) ) {
97+ return $ index - $ i ;
98+ } elseif ( $ i && $ this ->isWithinGroup ( $ statements , $ id , $ index + $ i ) ) {
99+ return $ index + $ i ;
100+ }
61101 }
62102
63- $ byPropertyIdArray = new ByPropertyIdArray ( $ statementList ->toArray () );
64- $ byPropertyIdArray ->buildIndex ();
65- $ byPropertyIdArray ->addObjectAtIndex ( $ statement , $ index );
66- $ this ->replaceStatements ( $ statementList , $ byPropertyIdArray ->toFlatArray () );
103+ return null ;
104+ }
105+
106+ /**
107+ * @param Statement[] $statements
108+ * @param int $index
109+ *
110+ * @return int|null
111+ */
112+ private function getClosestIndexAtGroupBorder ( array $ statements , $ index ) {
113+ $ longestDistance = max ( $ index , count ( $ statements ) - $ index );
114+
115+ for ( $ i = 0 ; $ i <= $ longestDistance ; $ i ++ ) {
116+ if ( $ this ->isGroupBorder ( $ statements , $ index - $ i ) ) {
117+ return $ index - $ i ;
118+ } elseif ( $ i && $ this ->isGroupBorder ( $ statements , $ index + $ i ) ) {
119+ return $ index + $ i ;
120+ }
121+ }
122+
123+ return null ;
124+ }
125+
126+ /**
127+ * @param Statement[] $statements
128+ * @param PropertyId $id
129+ * @param int $index
130+ *
131+ * @return bool
132+ */
133+ private function isWithinGroup ( array $ statements , PropertyId $ id , $ index ) {
134+ $ count = count ( $ statements );
135+
136+ // Valid if the index either prepends ot appends a statement with the same property ID
137+ return $ index > 0 && $ index <= $ count && $ statements [$ index - 1 ]->getPropertyId ()->equals ( $ id )
138+ || $ index >= 0 && $ index < $ count && $ statements [$ index ]->getPropertyId ()->equals ( $ id );
139+ }
140+
141+ /**
142+ * @param Statement[] $statements
143+ * @param int $index
144+ *
145+ * @return bool
146+ */
147+ private function isGroupBorder ( array $ statements , $ index ) {
148+ // First and last possible position is always a border
149+ return $ index <= 0
150+ || $ index >= count ( $ statements )
151+ || !$ statements [$ index - 1 ]->getPropertyId ()->equals ( $ statements [$ index ]->getPropertyId () );
67152 }
68153
69154}
0 commit comments