Project

General

Profile

« Previous | Next » 

Revision 21adfb98

Added by Andreas Müller about 9 years ago

View differences:

eu.etaxonomy.taxeditor.editor/src/main/java/eu/etaxonomy/taxeditor/editor/validation/MarkerManager.java
1 1
package eu.etaxonomy.taxeditor.editor.validation;
2 2

  
3
import java.util.HashMap;
3
import java.util.ArrayList;
4 4
import java.util.List;
5 5
import java.util.Set;
6 6

  
......
13 13
import org.eclipse.core.runtime.CoreException;
14 14

  
15 15
import eu.etaxonomy.cdm.model.validation.EntityConstraintViolation;
16
import eu.etaxonomy.cdm.model.validation.EntityValidationResult;
16
import eu.etaxonomy.cdm.model.validation.EntityValidation;
17 17
import eu.etaxonomy.cdm.model.validation.Severity;
18 18

  
19 19
/**
20 20
 * A class responsible for refreshing problem markers coming from the CVI
21 21
 * (Cdmlib Validation Infrastructure).
22
 * 
22
 *
23 23
 * @author ayco_holleman
24
 * 
24
 *
25 25
 */
26

  
26 27
/*
27 28
 * See following for problems with icons in Problems view
28 29
 * http://stackoverflow.com
......
34 35
 * http
35 36
 * ://cubussapiens.hu/2010/11/markers-and-annotations-in-eclipse-for-error-feedback
36 37
 * /
37
 * 
38
 *
38 39
 * See here for difficulty of attaching markers to non-resources (files,
39 40
 * folders):
40 41
 * http://stackoverflow.com/questions/12493179/eclipse-virtual-resources
41 42
 */
42 43
public class MarkerManager {
43 44

  
44
	public static final String MARKER_TYPE_ID = "eu.etaxonomy.taxeditor.markers.validationerror";
45

  
46
	/**
47
	 * The primary key (id) of the EntityValidationResult record
48
	 */
49
	public static final String ATTRIB_DATABASE_ID = "databaseId";
50

  
51
	// The values of the following constants must correspond to the attributes
52
	// defined for the org.eclipse.core.resources.markers extension point in
53
	// plugin.xml
54

  
55
	/**
56
	 * A user-friendly description of the type of the entity
57
	 */
58
	public static final String ATTRIB_USER_FRIENDLY_TYPE_NAME = "userFriendlyTypeName";
59
	/**
60
	 * A user-friendly description of the entity
61
	 */
62
	public static final String ATTRIB_USER_FRIENDLY_DESCRIPTION = "userFriendlyDescription";
63
	/**
64
	 * The field whose value violated a constraint
65
	 */
66
	public static final String ATTRIB_USER_FRIENDLY_FIELD_NAME = "userFriendlyFieldName";
67
	/**
68
	 * The value violating a constraint
69
	 */
70
	public static final String ATTRIB_INVALID_VALUE = "invalidValue";
71
	/**
72
	 * The message from the {@link Validator} about what was wrong.
73
	 */
74
	public static final String ATTRIB_VALIDATOR_MESSAGE = "validatorMessage";
75
	/**
76
	 * The class of the {@link Validator} coding for the constraint
77
	 */
78
	public static final String ATTRIB_VALIDATOR_CLASS = "validatorClass";
79
	/**
80
	 * The class of the validated entity
81
	 */
82
	public static final String ATTRIB_ENTITY_CLASS = "entityClass";
83
	/**
84
	 * The id of the validated entity
85
	 */
86
	public static final String ATTRIB_ENTITY_ID = "entityId";
87

  
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;
93

  
94

  
95
	MarkerManager(List<EntityValidationResult> results) throws CoreException
96
	{
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);
102
		}
103
		this.results = results;
104
		this.resultMap = new HashMap<Integer, EntityValidationResult>();
105
		for (EntityValidationResult result : results) {
106
			resultMap.put(result.getId(), result);
107
		}
108
	}
109

  
110

  
111
	/**
112
	 * Delete all markers that refer to errors that do not exist any longer
113
	 * (i.e. the corresponding database record has been deleted).
114
	 * 
115
	 * @return The number of deleted markers
116
	 * 
117
	 * @throws CoreException
118
	 */
119
	int deleteObsoleteMarkers() throws CoreException
120
	{
121
		int i = 0;
122
		IMarker[] markers = root.findMarkers(MARKER_TYPE_ID, true, IResource.DEPTH_INFINITE);
123
		for (IMarker marker : markers) {
124
			if (isObsoleteMarker(marker)) {
125
				++i;
126
				marker.delete();
127
			}
128
		}
129
		return i;
130
	}
131

  
132

  
133
	/**
134
	 * Create markers for new errors (i.e. no marker has been created for them
135
	 * yet).
136
	 * 
137
	 * @return The number of new markers
138
	 * 
139
	 * @throws CoreException
140
	 */
141
	int createMarkers() throws CoreException
142
	{
143
		int i = 0;
144
		IMarker[] markers = root.findMarkers(MARKER_TYPE_ID, true, IResource.DEPTH_INFINITE);
145
		for (EntityValidationResult result : results) {
146
			if (!isNewResult(result)) {
147
				continue;
148
			}
149
			Set<EntityConstraintViolation> problems = result.getEntityConstraintViolations();
150
			for (EntityConstraintViolation problem : problems) {
151
				if (markerExistsForProblem(problem, markers)) {
152
					continue;
153
				}
154
				IMarker marker = root.createMarker(MARKER_TYPE_ID);
155
				++i;
156
				if (problem.getSeverity() == Severity.ERROR) {
157
					marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
158
				}
159
				else if (problem.getSeverity() == Severity.WARNING) {
160
					marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_WARNING);
161
				}
162
				else {
163
					marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_INFO);
164
				}
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());
175
			}
176
		}
177
		return i;
178
	}
179

  
180

  
181
	/**
182
	 * Is there a problem marker that captures the specified
183
	 * {@link EntityConstraintViolation}? See
184
	 * {@link #markerCapturesProblem(IMarker, EntityConstraintViolation)}.
185
	 * 
186
	 * @param problem
187
	 * @param markers
188
	 * @return
189
	 * @throws CoreException
190
	 */
191
	private boolean markerExistsForProblem(EntityConstraintViolation problem, IMarker[] markers) throws CoreException
192
	{
193
		for (IMarker marker : markers) {
194
			if (markerCapturesProblem(marker, problem)) {
195
				return true;
196
			}
197
		}
198
		return false;
199
	}
200

  
201

  
202
	/**
203
	 * <p>
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.
214
	 * </p>
215
	 * <p>
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).
221
	 * </p>
222
	 * 
223
	 * @param marker
224
	 * @param problem
225
	 * @return
226
	 * @throws CoreException
227
	 */
228
	private static boolean markerCapturesProblem(IMarker marker, EntityConstraintViolation problem) throws CoreException
229
	{
230
		EntityValidationResult result = problem.getEntityValidationResult();
231
		if (!marker.getAttribute(ATTRIB_ENTITY_CLASS).equals(result.getValidatedEntityClass())) {
232
			return false;
233
		}
234
		if (!marker.getAttribute(ATTRIB_ENTITY_ID).equals(result.getValidatedEntityId())) {
235
			return false;
236
		}
237
		if (!marker.getAttribute(ATTRIB_USER_FRIENDLY_FIELD_NAME).equals(problem.getPropertyPath())) {
238
			return false;
239
		}
240
		if (!marker.getAttribute(ATTRIB_INVALID_VALUE).equals(problem.getInvalidValue())) {
241
			return false;
242
		}
243
		if (!marker.getAttribute(ATTRIB_VALIDATOR_CLASS).equals(problem.getValidator())) {
244
			return false;
245
		}
246
		return true;
247
	}
248

  
249

  
250
	/**
251
	 * Is this a marker without a corresponding database record (
252
	 * {@link EntityValidationResult})?
253
	 * 
254
	 * @param marker
255
	 * @return
256
	 * @throws CoreException
257
	 */
258
	private boolean isObsoleteMarker(IMarker marker) throws CoreException
259
	{
260
		return resultMap.get(getDatabaseId(marker)) == null;
261
	}
262

  
263

  
264
	/**
265
	 * Is this an {@link EntityValidationResult} for which no marker has been
266
	 * created yet?
267
	 * 
268
	 * @param result
269
	 * @return
270
	 */
271
	private boolean isNewResult(EntityValidationResult result)
272
	{
273
		return markerMap.get(result.getId()) == null;
274
	}
275

  
276

  
277
	/**
278
	 * Get the id of the {@link EntityValidationResult} that was stored as one
279
	 * of the marker's attributes.
280
	 * 
281
	 * @param marker
282
	 * @return
283
	 * @throws CoreException
284
	 */
285
	private static Integer getDatabaseId(IMarker marker) throws CoreException
286
	{
287
		return (Integer) marker.getAttribute(ATTRIB_DATABASE_ID);
288
	}
45
    public static final String MARKER_TYPE_ID = "eu.etaxonomy.taxeditor.markers.validationerror";
46

  
47
    /**
48
     * The primary key (id) of the EntityConstraintViolation record
49
     */
50
    public static final String ATTRIB_DATABASE_ID = "databaseId";
51

  
52
    // The values of the following constants must correspond to the attributes
53
    // defined for the org.eclipse.core.resources.markers extension point in
54
    // plugin.xml
55

  
56
    /**
57
     * A user-friendly description of the type of the entity
58
     */
59
    public static final String ATTRIB_USER_FRIENDLY_TYPE_NAME = "userFriendlyTypeName";
60
    /**
61
     * A user-friendly description of the entity
62
     */
63
    public static final String ATTRIB_USER_FRIENDLY_DESCRIPTION = "userFriendlyDescription";
64
    /**
65
     * The field whose value violated a constraint
66
     */
67
    public static final String ATTRIB_USER_FRIENDLY_FIELD_NAME = "userFriendlyFieldName";
68
    /**
69
     * The value violating a constraint
70
     */
71
    public static final String ATTRIB_INVALID_VALUE = "invalidValue";
72
    /**
73
     * The message from the {@link Validator} about what was wrong.
74
     */
75
    public static final String ATTRIB_VALIDATOR_MESSAGE = "validatorMessage";
76
    /**
77
     * The class of the {@link Validator} coding for the constraint
78
     */
79
    public static final String ATTRIB_VALIDATOR_CLASS = "validatorClass";
80
    /**
81
     * The class of the validated entity
82
     */
83
    public static final String ATTRIB_ENTITY_CLASS = "entityClass";
84
    /**
85
     * The id of the validated entity
86
     */
87
    public static final String ATTRIB_ENTITY_ID = "entityId";
88

  
89
    private final List<EntityConstraintViolation> problems;
90

  
91
    MarkerManager(List<EntityValidation> results) {
92
        this.problems = new ArrayList<EntityConstraintViolation>();
93
        for (EntityValidation result : results) {
94
            Set<EntityConstraintViolation> problemsPerEntity = result.getEntityConstraintViolations();
95
            for (EntityConstraintViolation problem : problemsPerEntity) {
96
                problem.setEntityValidation(result);
97
                problems.add(problem);
98
            }
99
        }
100
        //MessagingUtils.info("Number of validation errors: " + problems.size());
101
    }
102

  
103
    /**
104
     * Delete all markers that refer to errors that do not exist any longer
105
     * (i.e. the corresponding database record has been deleted).
106
     *
107
     * @return The number of deleted markers
108
     *
109
     * @throws CoreException
110
     */
111
    int deleteObsoleteMarkers() throws CoreException {
112
        int i = 0;
113
        IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
114
        IMarker[] markers = root.findMarkers(MARKER_TYPE_ID, true, IResource.DEPTH_INFINITE);
115
        for (IMarker marker : markers) {
116
            if (isObsoleteMarker(marker)) {
117
                ++i;
118
                marker.delete();
119
            }
120
        }
121
        //MessagingUtils.info("Obsolete markers: " + i);
122
        return i;
123
    }
124

  
125
    /**
126
     * Create markers for new errors (i.e. no marker has been created for them
127
     * yet).
128
     *
129
     * @return The number of new markers
130
     *
131
     * @throws CoreException
132
     */
133
    int createMarkers() throws CoreException {
134
        int i = 0;
135
        IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
136
        IMarker[] markers = root.findMarkers(MARKER_TYPE_ID, true, IResource.DEPTH_INFINITE);
137
        for (EntityConstraintViolation problem : problems) {
138
            if (isNewProblem(problem, markers)) {
139
                ++i;
140
                IMarker marker = root.createMarker(MARKER_TYPE_ID);
141
                if (problem.getSeverity() == Severity.ERROR) {
142
                    marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
143
                } else if (problem.getSeverity() == Severity.WARNING) {
144
                    marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_WARNING);
145
                } else {
146
                    marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_INFO);
147
                }
148
                EntityValidation result = problem.getEntityValidation();
149
                marker.setAttribute(IMarker.MESSAGE, problem.getMessage());
150
                marker.setAttribute(ATTRIB_DATABASE_ID, new Integer(problem.getId()));
151
                marker.setAttribute(ATTRIB_USER_FRIENDLY_TYPE_NAME, result.getUserFriendlyTypeName());
152
                marker.setAttribute(ATTRIB_USER_FRIENDLY_DESCRIPTION, result.getUserFriendlyDescription());
153
                marker.setAttribute(ATTRIB_USER_FRIENDLY_FIELD_NAME, problem.getUserFriendlyFieldName());
154
                marker.setAttribute(ATTRIB_VALIDATOR_MESSAGE, problem.getMessage());
155
                marker.setAttribute(ATTRIB_INVALID_VALUE, problem.getInvalidValue());
156
                marker.setAttribute(ATTRIB_VALIDATOR_CLASS, problem.getValidator());
157
                marker.setAttribute(ATTRIB_ENTITY_CLASS, result.getValidatedEntityClass());
158
                marker.setAttribute(ATTRIB_ENTITY_ID, result.getValidatedEntityId());
159
            }
160
        }
161
        //MessagingUtils.info("New problems: " + i);
162
        return i;
163
    }
164

  
165
    private boolean isObsoleteMarker(IMarker marker) throws CoreException {
166
        for (EntityConstraintViolation problem : problems) {
167
            if (isMarkerForProblem(marker, problem)) {
168
                return false;
169
            }
170
        }
171
        return true;
172
    }
173

  
174
    private static boolean isNewProblem(EntityConstraintViolation problem, IMarker[] markers) throws CoreException {
175
        for (IMarker marker : markers) {
176
            if (isMarkerForProblem(marker, problem)) {
177
                return false;
178
            }
179
        }
180
        return true;
181
    }
182

  
183
    private static boolean isMarkerForProblem(IMarker marker, EntityConstraintViolation problem) throws CoreException {
184
        if (isEqual(marker, ATTRIB_DATABASE_ID, new Integer(problem.getId()))) {
185
            return true;
186
        }
187
        EntityValidation result = problem.getEntityValidation();
188
        if (!isEqual(marker, ATTRIB_ENTITY_ID, result.getValidatedEntityId())) {
189
            return false;
190
        }
191
        if (!isEqual(marker, ATTRIB_INVALID_VALUE, problem.getInvalidValue())) {
192
            return false;
193
        }
194
        if (!isEqual(marker, ATTRIB_ENTITY_CLASS, result.getValidatedEntityClass())) {
195
            return false;
196
        }
197
        if (!isEqual(marker, ATTRIB_USER_FRIENDLY_FIELD_NAME, problem.getUserFriendlyFieldName())) {
198
            return false;
199
        }
200
        if (!isEqual(marker, ATTRIB_VALIDATOR_CLASS, problem.getValidator())) {
201
            return false;
202
        }
203
        return true;
204
    }
205

  
206
    private static boolean isEqual(IMarker marker, String attribute, Object value) throws CoreException {
207
        Object val = marker.getAttribute(attribute);
208
        boolean equal;
209
        if (val == null) {
210
            equal = value == null;
211
        } else {
212
            equal = value != null && val.equals(value);
213
        }
214
        return equal;
215
    }
289 216

  
290 217
}

Also available in: Unified diff