1 package eu
.etaxonomy
.taxeditor
.editor
.validation
;
3 import java
.util
.HashMap
;
7 import javax
.xml
.validation
.Validator
;
9 import org
.eclipse
.core
.resources
.IMarker
;
10 import org
.eclipse
.core
.resources
.IResource
;
11 import org
.eclipse
.core
.resources
.IWorkspaceRoot
;
12 import org
.eclipse
.core
.resources
.ResourcesPlugin
;
13 import org
.eclipse
.core
.runtime
.CoreException
;
15 import eu
.etaxonomy
.cdm
.model
.validation
.EntityConstraintViolation
;
16 import eu
.etaxonomy
.cdm
.model
.validation
.EntityValidationResult
;
17 import eu
.etaxonomy
.cdm
.model
.validation
.Severity
;
20 * A class responsible for refreshing problem markers coming from the CVI
21 * (Cdmlib Validation Infrastructure).
23 * @author ayco_holleman
27 * See following for problems with icons in Problems view
28 * http://stackoverflow.com
29 * /questions/13497258/markers-view-shows-my-marker-with-
30 * a-red-square-insted-of-error-warning-icon
31 * http://stackoverflow.com/questions/2888207
32 * /eclipse-plugin-custom-icon-for-a-marker
33 * http://www.eclipse.org/articles/Article-Mark%20My%20Words/mark-my-words.html
35 * ://cubussapiens.hu/2010/11/markers-and-annotations-in-eclipse-for-error-feedback
38 * See here for difficulty of attaching markers to non-resources (files,
40 * http://stackoverflow.com/questions/12493179/eclipse-virtual-resources
42 public class MarkerManager
{
44 public static final String MARKER_TYPE_ID
= "eu.etaxonomy.taxeditor.markers.validationerror";
47 * The primary key (id) of the EntityValidationResult record
49 public static final String ATTRIB_DATABASE_ID
= "databaseId";
51 // The values of the following constants must correspond to the attributes
52 // defined for the org.eclipse.core.resources.markers extension point in
56 * A user-friendly description of the type of the entity
58 public static final String ATTRIB_USER_FRIENDLY_TYPE_NAME
= "userFriendlyTypeName";
60 * A user-friendly description of the entity
62 public static final String ATTRIB_USER_FRIENDLY_DESCRIPTION
= "userFriendlyDescription";
64 * The field whose value violated a constraint
66 public static final String ATTRIB_USER_FRIENDLY_FIELD_NAME
= "userFriendlyFieldName";
68 * The value violating a constraint
70 public static final String ATTRIB_INVALID_VALUE
= "invalidValue";
72 * The message from the {@link Validator} about what was wrong.
74 public static final String ATTRIB_VALIDATOR_MESSAGE
= "validatorMessage";
76 * The class of the {@link Validator} coding for the constraint
78 public static final String ATTRIB_VALIDATOR_CLASS
= "validatorClass";
80 * The class of the validated entity
82 public static final String ATTRIB_ENTITY_CLASS
= "entityClass";
84 * The id of the validated entity
86 public static final String ATTRIB_ENTITY_ID
= "entityId";
88 private final IWorkspaceRoot root
;
89 private final IMarker
[] markers
;
90 private final List
<EntityValidationResult
> results
;
91 private final HashMap
<Integer
, EntityValidationResult
> resultMap
;
92 private final HashMap
<Integer
, IMarker
> markerMap
;
95 MarkerManager(List
<EntityValidationResult
> results
) throws CoreException
97 this.root
= ResourcesPlugin
.getWorkspace().getRoot();
98 this.markers
= root
.findMarkers(MARKER_TYPE_ID
, true, IResource
.DEPTH_INFINITE
);
99 this.markerMap
= new HashMap
<Integer
, IMarker
>();
100 for (IMarker marker
: markers
) {
101 markerMap
.put(getDatabaseId(marker
), marker
);
103 this.results
= results
;
104 this.resultMap
= new HashMap
<Integer
, EntityValidationResult
>();
105 for (EntityValidationResult result
: results
) {
106 resultMap
.put(result
.getId(), result
);
112 * Delete all markers that refer to errors that do not exist any longer
113 * (i.e. the corresponding database record has been deleted).
115 * @return The number of deleted markers
117 * @throws CoreException
119 int deleteObsoleteMarkers() throws CoreException
122 IMarker
[] markers
= root
.findMarkers(MARKER_TYPE_ID
, true, IResource
.DEPTH_INFINITE
);
123 for (IMarker marker
: markers
) {
124 if (isObsoleteMarker(marker
)) {
134 * Create markers for new errors (i.e. no marker has been created for them
137 * @return The number of new markers
139 * @throws CoreException
141 int createMarkers() throws CoreException
144 IMarker
[] markers
= root
.findMarkers(MARKER_TYPE_ID
, true, IResource
.DEPTH_INFINITE
);
145 for (EntityValidationResult result
: results
) {
146 if (!isNewResult(result
)) {
149 Set
<EntityConstraintViolation
> problems
= result
.getEntityConstraintViolations();
150 for (EntityConstraintViolation problem
: problems
) {
151 if (markerExistsForProblem(problem
, markers
)) {
154 IMarker marker
= root
.createMarker(MARKER_TYPE_ID
);
156 if (problem
.getSeverity() == Severity
.ERROR
) {
157 marker
.setAttribute(IMarker
.SEVERITY
, IMarker
.SEVERITY_ERROR
);
159 else if (problem
.getSeverity() == Severity
.WARNING
) {
160 marker
.setAttribute(IMarker
.SEVERITY
, IMarker
.SEVERITY_WARNING
);
163 marker
.setAttribute(IMarker
.SEVERITY
, IMarker
.SEVERITY_INFO
);
165 marker
.setAttribute(IMarker
.MESSAGE
, problem
.getMessage());
166 marker
.setAttribute(ATTRIB_DATABASE_ID
, result
.getId());
167 marker
.setAttribute(ATTRIB_USER_FRIENDLY_TYPE_NAME
, result
.getUserFriendlyTypeName());
168 marker
.setAttribute(ATTRIB_USER_FRIENDLY_DESCRIPTION
, result
.getUserFriendlyDescription());
169 marker
.setAttribute(ATTRIB_USER_FRIENDLY_FIELD_NAME
, problem
.getUserFriendlyFieldName());
170 marker
.setAttribute(ATTRIB_VALIDATOR_MESSAGE
, problem
.getMessage());
171 marker
.setAttribute(ATTRIB_INVALID_VALUE
, problem
.getInvalidValue());
172 marker
.setAttribute(ATTRIB_VALIDATOR_CLASS
, problem
.getValidator());
173 marker
.setAttribute(ATTRIB_ENTITY_CLASS
, result
.getValidatedEntityClass());
174 marker
.setAttribute(ATTRIB_ENTITY_ID
, result
.getValidatedEntityId());
182 * Is there a problem marker that captures the specified
183 * {@link EntityConstraintViolation}? See
184 * {@link #markerCapturesProblem(IMarker, EntityConstraintViolation)}.
189 * @throws CoreException
191 private boolean markerExistsForProblem(EntityConstraintViolation problem
, IMarker
[] markers
) throws CoreException
193 for (IMarker marker
: markers
) {
194 if (markerCapturesProblem(marker
, problem
)) {
204 * This method determines whether the problem exposed by the specified
205 * marker is <b>de facto</b> equivalent to the specified
206 * {@code EntityConstraintViolation}. When the CVI validates an entity, it
207 * first deletes previous validation results for that entity and only then
208 * saves the new validation result. Thus you cannot rely on the database id
209 * of the {@code EntityConstraintViolation} to determine equivalence. Maybe
210 * later we can make the CVI more sophisticated in this respect. Or maybe
211 * see if solving it through the equals() method of
212 * {@code EntityValidationResult} and/or {@code EntityConstraintViolation}
213 * is possible. But for now this is the easiest solution.
216 * The reason we check for equivalence, is that we don't want to
217 * unnecessarily update the Problems view. If a marker is there, we don't
218 * want to replace it with an equivalent marker, because that might lead to
219 * strange click behaviour for end users (e.g. selected problems will
220 * disappear and re-appear unselected).
226 * @throws CoreException
228 private static boolean markerCapturesProblem(IMarker marker
, EntityConstraintViolation problem
) throws CoreException
230 EntityValidationResult result
= problem
.getEntityValidationResult();
231 if (!marker
.getAttribute(ATTRIB_ENTITY_CLASS
).equals(result
.getValidatedEntityClass())) {
234 if (!marker
.getAttribute(ATTRIB_ENTITY_ID
).equals(result
.getValidatedEntityId())) {
237 if (!marker
.getAttribute(ATTRIB_USER_FRIENDLY_FIELD_NAME
).equals(problem
.getPropertyPath())) {
240 if (!marker
.getAttribute(ATTRIB_INVALID_VALUE
).equals(problem
.getInvalidValue())) {
243 if (!marker
.getAttribute(ATTRIB_VALIDATOR_CLASS
).equals(problem
.getValidator())) {
251 * Is this a marker without a corresponding database record (
252 * {@link EntityValidationResult})?
256 * @throws CoreException
258 private boolean isObsoleteMarker(IMarker marker
) throws CoreException
260 return resultMap
.get(getDatabaseId(marker
)) == null;
265 * Is this an {@link EntityValidationResult} for which no marker has been
271 private boolean isNewResult(EntityValidationResult result
)
273 return markerMap
.get(result
.getId()) == null;
278 * Get the id of the {@link EntityValidationResult} that was stored as one
279 * of the marker's attributes.
283 * @throws CoreException
285 private static Integer
getDatabaseId(IMarker marker
) throws CoreException
287 return (Integer
) marker
.getAttribute(ATTRIB_DATABASE_ID
);