001/*-------------------------------------------------------------------------+
002|                                                                          |
003| Copyright 2005-2011 the ConQAT Project                                   |
004|                                                                          |
005| Licensed under the Apache License, Version 2.0 (the "License");          |
006| you may not use this file except in compliance with the License.         |
007| You may obtain a copy of the License at                                  |
008|                                                                          |
009|    http://www.apache.org/licenses/LICENSE-2.0                            |
010|                                                                          |
011| Unless required by applicable law or agreed to in writing, software      |
012| distributed under the License is distributed on an "AS IS" BASIS,        |
013| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
014| See the License for the specific language governing permissions and      |
015| limitations under the License.                                           |
016+-------------------------------------------------------------------------*/
017package org.conqat.engine.commons.findings.location;
018
019import org.conqat.lib.commons.assertion.CCSMAssert;
020import org.conqat.lib.commons.js_export.ExportToJavaScript;
021
022import com.fasterxml.jackson.annotation.JsonCreator;
023import com.fasterxml.jackson.annotation.JsonProperty;
024
025/**
026 * This class denotes a region of text in an element.
027 *
028 * <b>Context:</b> Due to the way ConQAT deals with text, the class is a little
029 * bit more complex than expected. First, in ConQAT all text is normalized to
030 * use Unix style line endings (regardless of the line endings in the file).
031 * Second, ConQAT may apply filters, i.e. the internal (filtered) text
032 * representation may be different from the (raw) text in the file.
033 * Additionally, a location is best described by character offsets into the
034 * string, while a user typically expects line numbers. Conversion between all
035 * these representations is easy, as long as ConQAT internal representation is
036 * available. Without it, conversion is not possible.
037 *
038 * <b>Rationale:</b> When findings are reported to a user, the raw offsets
039 * and/or lines should be used, as these are more meaningful (visible in other
040 * editors as well). Also for persisting in a report, the raw positions are
041 * preferred, as the filtered ones depend on the ConQAT configuration, while raw
042 * offsets are independent of filter configuration. When working with findings
043 * within ConQAT, typically the filtered positions are needed, as most
044 * processors also work on the filtered representation. However, in such a case
045 * the corresponding element is typically available and thus conversion to the
046 * filtered representation is easy.
047 *
048 * <b>Implementation:</b> The finding (as well as the findings report) only
049 * stores raw positions. While the offsets would be sufficient, we also store
050 * line numbers to be able to provide meaningful user output. Filtered positions
051 * are not stored, but are made available via utility methods in the resource
052 * bundle. All fields are mandatory, i.e., it is not allowed to fill any
053 * position entry with invalid data (contrary to the old CodeRegionLocation,
054 * where -1 could be used to denote missing information).
055 */
056@ExportToJavaScript
057public class TextRegionLocation extends ElementLocation {
058
059        /**
060         * Version used for serialization.
061         */
062        private static final long serialVersionUID = 1;
063
064        /**
065         * An unknown offset used to indicate, that the line information is to be used
066         * to calculate character offset from.
067         */
068        public static final int UNKNOWN_TEXT_REGION_OFFSET = -1;
069
070        /**
071         * The name of the JSON property name for {@link #rawStartOffset}.
072         */
073        private static final String RAW_START_OFFSET_PROPERTY = "rawStartOffset";
074
075        /**
076         * The name of the JSON property name for {@link #rawEndOffset}.
077         */
078        private static final String RAW_END_OFFSET_PROPERTY = "rawEndOffset";
079
080        /**
081         * The name of the JSON property name for {@link #rawStartLine}.
082         */
083        private static final String RAW_START_LINE_PROPERTY = "rawStartLine";
084
085        /**
086         * The name of the JSON property name for {@link #rawEndLine}.
087         */
088        private static final String RAW_END_LINE_PROPERTY = "rawEndLine";
089
090        /**
091         * The absolute start position of the region in the (raw) text (zero based,
092         * inclusive).
093         */
094        @JsonProperty(RAW_START_OFFSET_PROPERTY)
095        private final int rawStartOffset;
096
097        /**
098         * The absolute end position in the (raw) text (zero based, inclusive).
099         */
100        @JsonProperty(RAW_END_OFFSET_PROPERTY)
101        private final int rawEndOffset;
102
103        /**
104         * The line corresponding to {@link #rawStartOffset} (one-based, inclusive).
105         */
106        @JsonProperty(RAW_START_LINE_PROPERTY)
107        private final int rawStartLine;
108
109        /**
110         * The line corresponding to {@link #rawEndOffset} (one-based, inclusive).
111         */
112        @JsonProperty(RAW_END_LINE_PROPERTY)
113        private final int rawEndLine;
114
115        /**
116         * Constructor.
117         */
118        @JsonCreator
119        public TextRegionLocation(@JsonProperty(LOCATION_PROPERTY) String location,
120                        @JsonProperty(UNIFORM_PATH_PROPERTY) String uniformPath,
121                        @JsonProperty(RAW_START_OFFSET_PROPERTY) int rawStartOffset,
122                        @JsonProperty(RAW_END_OFFSET_PROPERTY) int rawEndOffset,
123                        @JsonProperty(RAW_START_LINE_PROPERTY) int rawStartLine,
124                        @JsonProperty(RAW_END_LINE_PROPERTY) int rawEndLine) {
125                super(location, uniformPath);
126
127                CCSMAssert.isTrue(rawStartOffset <= rawEndOffset, "Start offset may not be after end offset.");
128                CCSMAssert.isTrue(rawStartLine <= rawEndLine, "Start line may not be after end line.");
129
130                this.rawStartOffset = rawStartOffset;
131                this.rawEndOffset = rawEndOffset;
132                this.rawStartLine = rawStartLine;
133                this.rawEndLine = rawEndLine;
134        }
135
136        /**
137         * Returns the absolute start position of the region in the (raw) text (zero
138         * based, inclusive).
139         */
140        public int getRawStartOffset() {
141                return rawStartOffset;
142        }
143
144        /**
145         * Returns the absolute end position in the (raw) text (zero based, inclusive).
146         */
147        public int getRawEndOffset() {
148                return rawEndOffset;
149        }
150
151        /**
152         * Returns the line corresponding to {@link #getRawStartOffset()} (one-based,
153         * inclusive).
154         */
155        public int getRawStartLine() {
156                return rawStartLine;
157        }
158
159        /**
160         * Returns the line corresponding to {@link #getRawEndOffset()} (one-based,
161         * inclusive).
162         */
163        public int getRawEndLine() {
164                return rawEndLine;
165        }
166
167        /**
168         * {@inheritDoc}
169         * <p>
170         * This includes the start and end line which is typically sufficient for
171         * debugging and showing to a user.
172         */
173        @Override
174        public String toLocationString() {
175                return super.toLocationString() + ":" + rawStartLine + "-" + rawEndLine;
176        }
177
178}