/*******************************************************************************
* Copyright (c) 2015 - 2016
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*******************************************************************************/
package jsettlers.mapcreator.mapvalidator.result;
import java.util.ArrayList;
import java.util.List;
import jsettlers.common.position.ShortPoint2D;
import jsettlers.mapcreator.mapvalidator.result.fix.AbstractFix;
/**
* List with validation errors
*
* @author Andreas Butti
*
*/
public class ValidationList {
/**
* Group with one header and multiple errors
*/
private static class Group {
/**
* Header of this group
*/
private final ErrorHeader header;
/**
* Special entries without position
*/
private final List<ErrorEntry> entriesWithoutPositions = new ArrayList<>();
/**
* Entries of this group
*/
private final List<ErrorEntry> entries = new ArrayList<>();
/**
* Constructor
*
* @param header
* Header of this group
*/
public Group(ErrorHeader header) {
this.header = header;
}
/**
* Try to group events logically by position
*
* @return Grouped list
*/
public LocalGroupList groupSimilar() {
LocalGroupList list = new LocalGroupList();
for (ErrorEntry e : entries) {
list.putIntoGroup(e);
}
return list;
}
/**
* @return true if this group contains entries
*/
public boolean isEmpty() {
return entriesWithoutPositions.isEmpty() && entries.isEmpty();
}
/**
* @return true if this group contains at least one error, false if it contains only warnings
*/
public boolean containsErrors() {
for (ErrorEntry e : entriesWithoutPositions) {
if (e.isError()) {
return true;
}
}
for (ErrorEntry e : entries) {
if (e.isError()) {
return true;
}
}
return false;
}
}
/**
* Group with events near on the map
*/
private static class LocaleGroup {
/**
* Max distance on map
*/
private static final int MAX_DISTANCE = 10;
/**
* Entries of this group
*/
private final List<ErrorEntry> entries = new ArrayList<>();
/**
* left
*/
private int x1;
/**
* right
*/
private int x2;
/**
* top
*/
private int y1;
/**
* bottom
*/
private int y2;
/**
* Constructor
*
* @param entry
* Entry to group
*/
public LocaleGroup(ErrorEntry entry) {
this.entries.add(entry);
ShortPoint2D pos = entry.getPos();
int x = pos.x;
x1 = x;
x2 = x;
int y = pos.y;
y1 = y;
y2 = y;
}
/**
* Add an entry to the list
*
* @param entry
* Entry
*/
public void add(ErrorEntry entry) {
this.entries.add(entry);
ShortPoint2D pos = entry.getPos();
int x = pos.x;
x1 = Math.min(x1, x);
x2 = Math.max(x2, x);
int y = pos.y;
y1 = Math.min(y1, y);
y2 = Math.max(y2, y);
}
/**
* Check if this Entry fits into this group
*
* @param entry
* Entry
* @return true if yes
*/
public boolean matchesGroup(ErrorEntry entry) {
ShortPoint2D pos = entry.getPos();
return isPointInRange(pos.x, pos.y);
}
/**
* Checks if this point is near this group
*
* @param x
* X
* @param y
* Y
* @return true if yes
*/
public boolean isPointInRange(int x, int y) {
// left
if (x1 > x + MAX_DISTANCE) {
return false;
}
// right
if (x2 < x - MAX_DISTANCE) {
return false;
}
// top
if (y1 > y + MAX_DISTANCE) {
return false;
}
// bottom
if (y2 < y - MAX_DISTANCE) {
return false;
}
return true;
}
}
/**
* List of groups
*/
private static class LocalGroupList extends ArrayList<LocaleGroup> {
private static final long serialVersionUID = 1L;
/**
* Constructor
*/
public LocalGroupList() {
}
/**
* Put the entry in a group, if none is matching, create one
*
* @param e
*/
public void putIntoGroup(ErrorEntry e) {
for (LocaleGroup g : this) {
if (g.matchesGroup(e)) {
g.add(e);
return;
}
}
add(new LocaleGroup(e));
}
}
/**
* List with all grouped error entries
*/
private final List<Group> list = new ArrayList<>();
/**
* Current group, to current header
*/
private Group currentGroup = null;
/**
* Constructor
*/
public ValidationList() {
}
/**
* Add a header entry, will be automatically removed if there are no followed tasks
*
* @param header
* Header
* @param fix
* Fix, if any
*/
public void addHeader(String header, AbstractFix fix) {
currentGroup = new Group(new ErrorHeader(header, fix));
list.add(currentGroup);
}
/**
* Add an error entry
*
* @param additionalErrorData
* Used for special cases... Can be anything, needs a special implementation in the sidebar also
* @param text
* Text to display
* @param error
* true for error, false for warning
* @param pos
* Position
* @param typeId
* Type ID of the error, all errors of the same type at nearly the same position are grouped
*/
public void addError(Object additionalErrorData, String text, boolean error, ShortPoint2D pos, String typeId) {
ErrorEntry entry = new ErrorEntry(additionalErrorData, text, error, pos, typeId);
if (pos == null) {
currentGroup.entriesWithoutPositions.add(entry);
} else {
currentGroup.entries.add(entry);
}
}
/**
* Prepare the list for displaying in the JList
*
* @return Model to display
*/
public ValidationListModel toListModel() {
ValidationListModel model = new ValidationListModel();
for (Group g : list) {
if (g.isEmpty()) {
continue;
}
// Errors? Or only warnings?
final boolean error = g.containsErrors();
model.addElement(g.header);
LocalGroupList groupedList = g.groupSimilar();
for (LocaleGroup lgl : groupedList) {
// only use first entry of the group
ErrorEntry element = lgl.entries.get(0);
model.addElement(element);
}
// but add all special entries without position
for (ErrorEntry e : g.entriesWithoutPositions) {
model.addElement(e);
}
g.header.setError(error);
}
model.prepareToDisplay();
return model;
}
}