OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

Rev 83 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
17 ilm 1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
5
 *
6
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
7
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
8
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
9
 * language governing permissions and limitations under the License.
10
 *
11
 * When distributing the software, include this License Header Notice in each file.
12
 */
13
 
14
 package org.openconcerto.openoffice.spreadsheet;
15
 
73 ilm 16
import org.openconcerto.utils.CompareUtils;
17
 
17 ilm 18
import java.awt.Point;
156 ilm 19
import java.util.ArrayList;
20
import java.util.Collections;
21
import java.util.List;
17 ilm 22
import java.util.regex.Matcher;
156 ilm 23
import java.util.regex.Pattern;
17 ilm 24
 
25
/**
26
 * A cell range.
27
 *
28
 * @author Sylvain
29
 */
30
public final class Range {
31
 
156 ilm 32
    // added illegal characters to unquoted form from LibreOffice UI, especially ':' to avoid
33
    // mistakenly using it in the table name instead of as a separator
34
    private static final String tableNamePattern = "\\$?([^\\Q. '[]*?:/\\\\E]+|'([^']|'')+')";
35
    // added parens to capture cell addresses
36
    // \1 is sheet name, \4 cell address, \6 second sheet name, \9 second cell address
37
    private static final Pattern cellRangePattern = java.util.regex.Pattern.compile("^(" + tableNamePattern + ")?\\.(\\$?[A-Z]+\\$?[0-9]+)(:(" + tableNamePattern + ")?\\.(\\$?[A-Z]+\\$?[0-9]+))?");
38
    private static final Pattern LIST_SEPARATOR_PATTERN = Pattern.compile("^\\s+");
39
 
17 ilm 40
    /**
41
     * Parse a range.
42
     *
43
     * @param range the string form, e.g. "Sheet1.A23:.AA34".
44
     * @return the parsed range.
45
     */
46
    static public final Range parse(String range) {
156 ilm 47
        final Matcher m = cellRangePattern.matcher(range);
17 ilm 48
        if (!m.matches())
49
            throw new IllegalStateException(range + " is not a valid range address");
156 ilm 50
        return parse(m);
51
    }
52
 
53
    static private final Range parse(final Matcher m) {
17 ilm 54
        final String sheet1 = SpreadSheet.parseSheetName(m.group(1));
55
        final String sheet2 = SpreadSheet.parseSheetName(m.group(6));
56
 
57
        final Point start = Table.resolve(m.group(4));
83 ilm 58
        final String cell2 = m.group(9);
59
        final Point end = cell2 == null ? null : Table.resolve(cell2);
17 ilm 60
 
61
        return new Range(sheet1, start, sheet2, end);
62
    }
63
 
156 ilm 64
    // 9.2.5 Cell Range Address List, used by "table:print-ranges" for example.
65
    // e.g. "Feuille1.A1:Feuille1.L65 Feuille1.A67:Feuille1.L107"
66
    static public final List<Range> parseList(String ranges) {
67
        if (ranges.isEmpty())
68
            return Collections.emptyList();
69
 
70
        final Matcher m = cellRangePattern.matcher(ranges);
71
        m.useAnchoringBounds(true);
72
        if (!m.find())
73
            throw new IllegalStateException(ranges + " is not a valid range address list");
74
        final Range first = parse(m);
75
        final int length = ranges.length();
76
        if (length == m.end())
77
            return Collections.singletonList(first);
78
 
79
        final List<Range> res = new ArrayList<>();
80
        res.add(first);
81
        final Matcher sepMatcher = LIST_SEPARATOR_PATTERN.matcher(ranges);
82
        sepMatcher.useAnchoringBounds(true);
83
        while (m.end() != length) {
84
            sepMatcher.region(m.end(), length);
85
            if (!sepMatcher.find())
86
                throw new IllegalStateException("Couldn't find separator at " + sepMatcher.regionStart() + " in " + ranges);
87
            m.region(sepMatcher.end(), length);
88
            if (!m.find())
89
                throw new IllegalStateException("Couldn't find range address at " + m.regionStart() + " in " + ranges);
90
            res.add(parse(m));
91
        }
92
        return res;
93
    }
94
 
17 ilm 95
    private final String sheet1, sheet2;
96
    private final Point start, end;
97
 
98
    /**
99
     * Create a new instance with a single cell.
100
     *
101
     * @param sheet name of the sheet.
102
     * @param point coordinate of the cell.
103
     */
104
    public Range(String sheet, Point point) {
105
        this(sheet, point, point);
106
    }
107
 
108
    public Range(String sheet, Point startPoint, Point endPoint) {
109
        this(sheet, startPoint, null, endPoint);
110
    }
111
 
112
    /**
113
     * Create a new instance.
114
     *
115
     * @param startSheet name of the start sheet.
116
     * @param startPoint coordinate of the start.
117
     * @param endSheet name of the end sheet, can be <code>null</code> if the range doesn't span
118
     *        multiple sheets.
83 ilm 119
     * @param endPoint coordinate of the end, can be <code>null</code> for single cell range.
17 ilm 120
     */
121
    public Range(String startSheet, Point startPoint, String endSheet, Point endPoint) {
122
        super();
73 ilm 123
        if (startSheet == null && endSheet != null)
124
            throw new NullPointerException("null start sheet, but non null endSheet : " + endSheet);
17 ilm 125
        this.sheet1 = startSheet;
126
        this.sheet2 = endSheet == null ? startSheet : endSheet;
83 ilm 127
        if (startPoint == null)
128
            throw new NullPointerException("Null start point");
17 ilm 129
        this.start = startPoint;
83 ilm 130
        if (endPoint == null && spanSheets())
131
            throw new IllegalArgumentException("End cell must be passed for range spanning sheets : " + getStartSheet() + " to " + getEndSheet());
132
        this.end = endPoint == null ? this.start : endPoint;
133
        assert this.start != null && this.end != null;
17 ilm 134
    }
135
 
136
    public final String getStartSheet() {
137
        return this.sheet1;
138
    }
139
 
140
    public final Point getStartPoint() {
141
        return this.start;
142
    }
143
 
144
    public final String getEndSheet() {
145
        return this.sheet2;
146
    }
147
 
148
    public final Point getEndPoint() {
149
        return this.end;
150
    }
151
 
152
    public final boolean spanSheets() {
73 ilm 153
        return !CompareUtils.equals(this.getStartSheet(), this.getEndSheet());
17 ilm 154
    }
155
 
156
    @Override
157
    public boolean equals(Object obj) {
158
        if (this == obj)
159
            return true;
160
        if (obj == null)
161
            return false;
162
        if (getClass() != obj.getClass())
163
            return false;
164
        final Range o = (Range) obj;
73 ilm 165
        return CompareUtils.equals(this.sheet1, o.sheet1) && this.start.equals(o.start) && CompareUtils.equals(this.sheet2, o.sheet2) && this.end.equals(o.end);
17 ilm 166
    }
167
 
168
    @Override
169
    public int hashCode() {
170
        final int prime = 31;
171
        int result = 1;
172
        result = prime * result + ((this.sheet1 == null) ? 0 : this.sheet1.hashCode());
173
        result = prime * result + ((this.start == null) ? 0 : this.start.hashCode());
174
        result = prime * result + ((this.sheet2 == null) ? 0 : this.sheet2.hashCode());
175
        result = prime * result + ((this.end == null) ? 0 : this.end.hashCode());
176
        return result;
177
    }
178
 
179
    @Override
180
    public String toString() {
181
        final StringBuilder sb = new StringBuilder(32);
73 ilm 182
        if (this.getStartSheet() != null)
183
            sb.append(this.getStartSheet());
83 ilm 184
        // the RelaxNG pattern requires the dot
17 ilm 185
        sb.append(".");
186
        sb.append(Table.getAddress(getStartPoint()));
83 ilm 187
        if (this.spanSheets()) {
188
            sb.append(":");
17 ilm 189
            sb.append(this.getEndSheet());
83 ilm 190
            sb.append(".");
191
            sb.append(Table.getAddress(getEndPoint()));
192
        } else if (!getEndPoint().equals(getStartPoint())) {
193
            sb.append(":");
194
            // the RelaxNG pattern requires the dot
195
            sb.append(".");
196
            sb.append(Table.getAddress(getEndPoint()));
197
        }
17 ilm 198
        return sb.toString();
199
    }
200
}