Lines 18-26
Link Here
|
18 |
package org.apache.poi.xssf.usermodel.helpers; |
18 |
package org.apache.poi.xssf.usermodel.helpers; |
19 |
|
19 |
|
20 |
import java.util.ArrayList; |
20 |
import java.util.ArrayList; |
21 |
import java.util.HashSet; |
21 |
import java.util.Collection; |
|
|
22 |
import java.util.Collections; |
22 |
import java.util.List; |
23 |
import java.util.List; |
23 |
import java.util.Set; |
24 |
import java.util.Map; |
|
|
25 |
import java.util.Map.Entry; |
26 |
import java.util.TreeMap; |
24 |
|
27 |
|
25 |
import org.apache.poi.ss.formula.FormulaParseException; |
28 |
import org.apache.poi.ss.formula.FormulaParseException; |
26 |
import org.apache.poi.ss.formula.FormulaParser; |
29 |
import org.apache.poi.ss.formula.FormulaParser; |
Lines 71-83
Link Here
|
71 |
* @return an array of affected cell regions |
74 |
* @return an array of affected cell regions |
72 |
*/ |
75 |
*/ |
73 |
public List<CellRangeAddress> shiftMerged(int startRow, int endRow, int n) { |
76 |
public List<CellRangeAddress> shiftMerged(int startRow, int endRow, int n) { |
74 |
List<CellRangeAddress> shiftedRegions = new ArrayList<CellRangeAddress>(); |
77 |
Map<Integer, CellRangeAddress> modifiedRegions = new TreeMap<Integer, CellRangeAddress>(); |
75 |
Set<Integer> removedIndices = new HashSet<Integer>(); |
78 |
List<CellRangeAddress> removedRegions = new ArrayList<CellRangeAddress>(); |
|
|
79 |
Collection<Integer> modifiedIndices = new ArrayList<Integer>(); //overwritten |
80 |
|
81 |
if (n == 0) { |
82 |
return Collections.emptyList(); |
83 |
} |
84 |
|
85 |
int destStartRow = startRow + n; |
86 |
int destEndRow = endRow + n; |
87 |
|
76 |
//move merged regions completely if they fall within the new region boundaries when they are shifted |
88 |
//move merged regions completely if they fall within the new region boundaries when they are shifted |
77 |
int size = sheet.getNumMergedRegions(); |
89 |
final int numMergedRegions = sheet.getNumMergedRegions(); |
78 |
for (int i = 0; i < size; i++) { |
90 |
for (int i = 0; i < numMergedRegions; i++) { |
79 |
CellRangeAddress merged = sheet.getMergedRegion(i); |
91 |
CellRangeAddress merged = sheet.getMergedRegion(i); |
|
|
92 |
|
93 |
// Behavior: Unaffected |
94 |
// Condition: if shift source and destination rows do not overlap with merged region |
95 |
boolean mergedRegionNotInSourceRows = (endRow < merged.getFirstRow() || merged.getLastRow() < startRow); |
96 |
boolean mergedRegionNotInDestRows = (destEndRow < merged.getFirstRow() || merged.getLastRow() < destStartRow); |
97 |
if (mergedRegionNotInSourceRows && mergedRegionNotInDestRows) { |
98 |
continue; |
99 |
} |
100 |
// Condition: or if source and destination rows fully inside merged region |
101 |
boolean sourceRowsInMergedRegion = (merged.getFirstRow() < startRow && endRow < merged.getLastRow()); |
102 |
boolean destRowsInMergedRegion = (merged.getFirstRow() < destStartRow && destEndRow < merged.getLastRow()); |
103 |
if (sourceRowsInMergedRegion && destRowsInMergedRegion) { |
104 |
continue; |
105 |
} |
106 |
|
107 |
// CellRangeAddresses cannot be modified in place. |
108 |
// At this point, any CellRangeAddress will be modified or removed. |
109 |
modifiedIndices.add(i); |
110 |
|
111 |
// Behavior: Merged region gets deleted |
112 |
// Condition: if merged region is outside shift source rows but fully or partially contained within shift destination rows |
113 |
// Condition: if destination rows contain merged regions, merged regions would be shrunk or broken up. Remove them if there's any conflict. |
114 |
boolean mergedRegionInDestRows = (destStartRow <= merged.getLastRow() && merged.getFirstRow() <= destEndRow); |
115 |
if (mergedRegionInDestRows) { |
116 |
removedRegions.add(i, merged); |
117 |
continue; |
118 |
} |
119 |
|
120 |
// At this point, any CellRangeAddress will be modified. |
121 |
modifiedRegions.put(i, merged); |
122 |
|
123 |
// Behavior: Entire merged region gets shifted down |
124 |
// Condition: if shift source rows fully contain the merged region |
125 |
if (startRow <= merged.getFirstRow() && merged.getLastRow() <= endRow) { |
126 |
merged.setFirstRow(merged.getFirstRow() + n); |
127 |
merged.setLastRow(merged.getLastRow() + n); |
128 |
continue; |
129 |
} |
130 |
|
131 |
// Behavior: Merged region gets elongated |
132 |
// Condition: if startRow is inside the merged region and not the first row of the region (which would result in a shift) and n>0 |
133 |
if (n > 0) { |
134 |
boolean startRowInMergedRegion = merged.getFirstRow() < startRow && startRow <= merged.getLastRow(); |
135 |
boolean endRowAfterMergedRegion = merged.getLastRow() <= endRow; |
136 |
if (startRowInMergedRegion && endRowAfterMergedRegion) { |
137 |
merged.setLastRow(merged.getLastRow() + n); |
138 |
continue; |
139 |
} |
140 |
} |
141 |
else { |
142 |
// Condition: if endRow is inside the merged region and not the last row of the region (which would result in a shift) and n>0 |
143 |
boolean endRowInMergedRegion = merged.getFirstRow() <= endRow && endRow < merged.getLastRow(); |
144 |
boolean startRowBeforeMergedRegion = startRow <= merged.getFirstRow(); |
145 |
if (endRowInMergedRegion && startRowBeforeMergedRegion) { |
146 |
merged.setFirstRow(merged.getFirstRow() + n); |
147 |
continue; |
148 |
} |
149 |
} |
150 |
|
151 |
// Behavior: Merged region gets shrunk |
152 |
// Condition: startRow is moved down or endRow is moved up |
153 |
boolean mergedRegionShrunkFromTop = (startRow == merged.getFirstRow() && merged.getLastRow() <= destEndRow); |
154 |
if (mergedRegionShrunkFromTop) { |
155 |
merged.setFirstRow(destStartRow); |
156 |
continue; |
157 |
} |
158 |
boolean mergedRegionShrunkFromBottom = (destStartRow <= merged.getLastRow() && merged.getLastRow() == endRow); |
159 |
if (mergedRegionShrunkFromBottom) { |
160 |
merged.setLastRow(destEndRow); |
161 |
continue; |
162 |
} |
163 |
|
80 |
|
164 |
|
|
|
165 |
|
166 |
/* |
81 |
boolean inStart = (merged.getFirstRow() >= startRow || merged.getLastRow() >= startRow); |
167 |
boolean inStart = (merged.getFirstRow() >= startRow || merged.getLastRow() >= startRow); |
82 |
boolean inEnd = (merged.getFirstRow() <= endRow || merged.getLastRow() <= endRow); |
168 |
boolean inEnd = (merged.getFirstRow() <= endRow || merged.getLastRow() <= endRow); |
83 |
|
169 |
|
Lines 87-129
Link Here
|
87 |
} |
173 |
} |
88 |
|
174 |
|
89 |
//only shift if the region outside the shifted rows is not merged too |
175 |
//only shift if the region outside the shifted rows is not merged too |
90 |
if (!containsCell(merged, startRow - 1, 0) && !containsCell(merged, endRow + 1, 0)) { |
176 |
if (!merged.containsRow(startRow - 1) && !merged.containsRow(endRow + 1)) { |
91 |
merged.setFirstRow(merged.getFirstRow() + n); |
177 |
merged.setFirstRow(merged.getFirstRow() + n); |
92 |
merged.setLastRow(merged.getLastRow() + n); |
178 |
merged.setLastRow(merged.getLastRow() + n); |
93 |
//have to remove/add it back |
179 |
//have to remove/add it back |
94 |
shiftedRegions.add(merged); |
180 |
shiftedRegions.add(merged); |
95 |
removedIndices.add(i); |
181 |
removedIndices.add(i); |
96 |
} |
182 |
}*/ |
97 |
} |
183 |
} |
98 |
|
184 |
|
99 |
if(!removedIndices.isEmpty()) { |
185 |
// FIXME: would be more efficient to remove only the merged regions that were deleted |
100 |
sheet.removeMergedRegions(removedIndices); |
186 |
// and modify existing merged regions in-place (probably something like Sheet.replaceMergedRegion(int index, CellRangeAddress replacement)) |
|
|
187 |
|
188 |
for (Entry<Integer, CellRangeAddress> entry : modifiedRegions.entrySet()) { |
189 |
sheet.replaceMergedRegion(entry.getKey(), entry.getValue()); |
101 |
} |
190 |
} |
|
|
191 |
|
192 |
// Remove all merged regions that were modified or deleted |
193 |
if(!modifiedIndices.isEmpty()) { |
194 |
sheet.removeMergedRegions(modifiedIndices); |
195 |
} |
102 |
|
196 |
|
103 |
//read so it doesn't get shifted again |
197 |
/*// Add back merged regions that were modified |
104 |
for (CellRangeAddress region : shiftedRegions) { |
198 |
for (CellRangeAddress region : modifiedRegions) { |
105 |
sheet.addMergedRegion(region); |
199 |
sheet.addMergedRegion(region); |
106 |
} |
200 |
}*/ |
107 |
return shiftedRegions; |
201 |
// Include deleted regions in the return value |
|
|
202 |
List<CellRangeAddress> affectedRegions = new ArrayList<CellRangeAddress>(modifiedRegions.values()); |
203 |
affectedRegions.addAll(removedRegions); |
204 |
return affectedRegions; |
108 |
} |
205 |
} |
109 |
|
206 |
|
110 |
/** |
207 |
/** |
111 |
* Check if the row and column are in the specified cell range |
|
|
112 |
* |
113 |
* @param cr the cell range to check in |
114 |
* @param rowIx the row to check |
115 |
* @param colIx the column to check |
116 |
* @return true if the range contains the cell [rowIx,colIx] |
117 |
*/ |
118 |
private static boolean containsCell(CellRangeAddress cr, int rowIx, int colIx) { |
119 |
if (cr.getFirstRow() <= rowIx && cr.getLastRow() >= rowIx |
120 |
&& cr.getFirstColumn() <= colIx && cr.getLastColumn() >= colIx) { |
121 |
return true; |
122 |
} |
123 |
return false; |
124 |
} |
125 |
|
126 |
/** |
127 |
* Updated named ranges |
208 |
* Updated named ranges |
128 |
*/ |
209 |
*/ |
129 |
public void updateNamedRanges(FormulaShifter shifter) { |
210 |
public void updateNamedRanges(FormulaShifter shifter) { |