package com.iambookmaster.client.paragraph;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DeferredCommand;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.iambookmaster.client.Images;
import com.iambookmaster.client.Styles;
import com.iambookmaster.client.beans.Alchemy;
import com.iambookmaster.client.beans.Battle;
import com.iambookmaster.client.beans.Modificator;
import com.iambookmaster.client.beans.NPC;
import com.iambookmaster.client.beans.ObjectBean;
import com.iambookmaster.client.beans.Paragraph;
import com.iambookmaster.client.beans.ParagraphConnection;
import com.iambookmaster.client.beans.Parameter;
import com.iambookmaster.client.common.EditorTab;
import com.iambookmaster.client.common.MaskPanel;
import com.iambookmaster.client.common.ScrollContainer;
import com.iambookmaster.client.editor.ModelPersist;
import com.iambookmaster.client.exceptions.TimeoutException;
import com.iambookmaster.client.locale.AppConstants;
import com.iambookmaster.client.locale.AppLocale;
import com.iambookmaster.client.locale.AppMessages;
import com.iambookmaster.client.model.Model;
import com.iambookmaster.client.model.Model.FullParagraphDescriptonBuilder;
public class ParagraphValidator extends ScrollContainer implements EditorTab {
private final AppConstants appConstants = AppLocale.getAppConstants();
private final AppMessages appMessages = AppLocale.getAppMessages();
private VerticalPanel errorList;
private Model model;
private int errorCount;
public ParagraphValidator(Model mod) {
model = mod;
errorList = new VerticalPanel();
errorList.setStyleName("editor_panel");
errorList.setSpacing(2);
errorList.setSize("100%", "100%");
setScrollWidget(errorList);
resetHeight();
}
private void stopProgress() {
MaskPanel.hide();
}
public void startTesting(final boolean validateText,final boolean validateMap,final boolean deploy,
final ExportBookCallback callback) {
startProgress();
DeferredCommand.addCommand(new Command() {
public void execute() {
try {
validate(validateText,validateMap,false,deploy);
} catch (TimeoutException e) {
//we cannot be here in client side
}
stopProgress();
if (errorCount==0) {
callback.onSuccess(null);
} else {
callback.onError();
}
}
});
}
public void startTesting(final boolean validateText,final boolean validateMap) {
startProgress();
DeferredCommand.addCommand(new Command() {
public void execute() {
try {
validate(validateText,validateMap,false,false);
} catch (TimeoutException e) {
//we cannot be here on client side
}
stopProgress();
}
});
}
/**
* Export text of book
* @param reExport
* @return
*/
public void createText(final ExportBookCallback listener,final boolean reExport) {
startProgress();
DeferredCommand.addCommand(new Command() {
public void execute() {
try {
validate(true,reExport==false,false,true);
} catch (TimeoutException e) {
//we cannot be here on client size
}
if (errorCount==0) {
try {
String text = generateText(reExport);
if (text==null || errorCount>0) {
listener.onError();
} else {
listener.onSuccess(text);
}
} catch (TimeoutException e) {
listener.onError();
}
}
stopProgress();
}
});
}
private String generateText(boolean reExport) throws TimeoutException{
addMessage(appConstants.validatorBookGeneration(),false);
BookCreator creator = new BookCreator(model);
String text = creator.createText(reExport,new BookCreatorListener() {
public void algorithmError(int code) {
addError(appMessages.validatorAlgorithmError(code));
}
public void allIterationsFailed() {
addError(appConstants.validatorAllAttempsFailed());
}
public void iterationFailed(int fail, int total) {
addWarning(appMessages.validatorIterationFailed(fail,total));
}
public void numberNotSet(Paragraph paragraph) {
addError(appMessages.validationParagraphNumberNotSet(paragraph.getName()));
}
public void numberTooLarge(Paragraph paragraph, int max) {
addError(appMessages.validationParagraphNumberOutOfRange(paragraph.getName(),max));
}
public void numbersDuplicated(Paragraph paragraph, Paragraph paragraph2) {
addError(appMessages.validationParagraphNameDuplicated(paragraph.getName(),paragraph.getNumber(),paragraph.getName()));
}
public void noSupported() {
addError("Generation paragraph numbers is not supported on client side");
}
/**
* Always false in client mode
*/
public boolean checkTimiout() {
return false;
}
public void numberNotSet(ObjectBean objectBean) {
addError(appMessages.validationObjectDoesNotHaveSecretKey(objectBean.getName()));
}
public void wrongObjectSecretKey(ParagraphConnection connection) {
addError(appMessages.validationObjectHasWrongSecretKey(connection.getObject().getName(),connection.getObject().getKey(),connection.getFrom().getNumber(),connection.getFrom().getName(),connection.getTo().getNumber(),connection.getTo().getName()));
}
public void tooManyObjects() {
addError(appMessages.serverBookGenerationTooManyObjects(model.getObjects().size(),model.getParagraphs().size()));
}
});
addMessage("Done",true);
return text;
}
private void startProgress() {
errorCount = 0;
errorList.clear();
HTML html = new HTML(" ");
errorList.add(html);
errorList.setCellHeight(html,"99%");
MaskPanel.show();
}
private void validate(boolean validateText, boolean validateMap, final boolean checkSecretKeys, boolean deploy) throws TimeoutException{
//begin from Data validation
if (model instanceof ModelPersist) {
ModelPersist modelPersist = (ModelPersist) model;
modelPersist.checkIntegrity();
}
if (validateMap) {
addMessage(appConstants.validatorMapOfParagraphs(),false);
//map validation
PathFinder pathFinder = new PathFinder(model);
pathFinder.setCheckSuccessOnly(deploy);
pathFinder.validate(new ExtendedPathFinderErrorListener());
addMessage(appConstants.validatorDone(),true);
}
if (validateText) {
addMessage(appConstants.validatorContenValidation(),false);
ArrayList<Paragraph> paragrapghs = model.getParagraphs();
FullParagraphDescriptonBuilder builder = model.getFullParagraphDescriptonBuilder();
builder.setHiddenUsingObjects(checkSecretKeys);
builder.setEmptyConditionIsError(model.getSettings().isHiddenUsingObjects()==false);
for (int i = 0; i < paragrapghs.size(); i++) {
Paragraph paragraph = paragrapghs.get(i);
ArrayList<String> errors = new ArrayList<String>();
builder.getFullParagraphDescripton(paragraph, null, errors,null);
for (int j = 0; j < errors.size(); j++) {
addError(appMessages.validatorParagraphTextError(paragraph.getName(),errors.get(j)),paragraph);
}
}
addMessage(appConstants.validatorDone(),true);
}
//end
}
private void addMessage(String text,boolean end) {
new Message(text,end);
}
private void addError(String text) {
errorCount++;
new Message(text,Images.ERROR);
}
private void addWarning(String text) {
new Message(text,Images.WARNING);
}
private void addWarning(String text,ObjectBean object) {
new Message(text,Images.WARNING,object);
}
private void addError(String text,Paragraph paragraph) {
errorCount++;
new Message(text,Images.ERROR,paragraph);
}
private void addError(String text, ParagraphConnection connection) {
errorCount++;
new Message(text,Images.ERROR,connection);
}
private void addError(String text, ObjectBean object) {
errorCount++;
new Message(text,Images.ERROR,object);
}
public class Message extends HorizontalPanel implements ClickListener{
private static final String STYLE = "validation_line";
private static final String STYLE_END = "validation_line_end";
private Image img;
private Label label;
private Paragraph paragraph;
private ParagraphConnection connection;
private ObjectBean object;
public Message(String text, String image) {
setWidth("100%");
img = new Image(image);
img.setStyleName(STYLE);
add(img);
setCellWidth(img,"1%");
label = new Label(text);
label.setWidth("100%");
label.setStyleName(STYLE);
add(label);
setCellWidth(label,"99%");
errorList.insert(this,errorList.getWidgetCount()-1);
errorList.setCellWidth(this,"100%");
}
public Message(String text, String image, Paragraph paragraph) {
this(text,image);
makeClickable();
this.paragraph = paragraph;
}
private void makeClickable() {
img.addStyleName(Styles.CLICKABLE);
img.addClickListener(this);
label.addStyleName(Styles.CLICKABLE);
label.addClickListener(this);
}
public Message(String text, String image, ParagraphConnection connection) {
this(text,image);
makeClickable();
this.connection = connection;
}
public Message(String text, String image, ObjectBean object) {
this(text,image);
makeClickable();
this.object = object;
}
public Message(String text,boolean end) {
setWidth("100%");
label = new Label(text);
if (end) {
label.setStyleName(STYLE_END);
} else {
label.setStyleName(STYLE);
}
label.setWidth("100%");
add(label);
setCellWidth(label,"100%");
errorList.insert(this,errorList.getWidgetCount()-1);
errorList.setCellWidth(this,"100%");
}
public void onClick(Widget sender) {
if (paragraph != null) {
model.selectParagraph(paragraph, null);
model.editParagraph(paragraph, null);
} else if (connection != null) {
model.selectParagraphConnection(connection, null);
} else if (object != null) {
model.selectObject(object, null);
}
}
}
public void activate() {
resetHeight();
}
public void deactivate() {
}
public void close() {
}
public void findCommercialParagraph() {
startProgress();
for (Paragraph paragraph : model.getParagraphs()) {
paragraph.setCommercial(true);
}
addMessage(appConstants.validatorCommercialParagraphs(),false);
//searching commercial paragraphs
PathFinder pathFinder = new PathFinder(model) {
@Override
protected void passedParagraph(Paragraph current) {
current.setCommercial(false);
}
};
pathFinder.setCheckSuccessOnly(false);
try {
pathFinder.validate(new StandardPathFinderErrorListener(){
public void objectCannotBeFound(ObjectBean object) {
}
public void objectCannotBeUsed(ObjectBean bean) {
}
public void unriachebleParagraph(Paragraph bean) {
}
public void unusedParagraphConnection(ParagraphConnection bean) {
}
public void unusedModificator(Modificator modificator) {
}
public void unusedParameter(Parameter parameter) {
}
public void battleIsUsedNowhere(Battle battle) {
}
public void alchemyIsUsedNowhere(Alchemy alchemy) {
}
public void NPCIsUsedNowhere(NPC npc) {
}
public boolean canBePassed(ParagraphConnection connection, Paragraph paragraph) {
//commercial paragraphs cannot be passed
return paragraph.getType() != Paragraph.TYPE_COMMERCIAL;
}
public void noWayToSuccess(Paragraph paragraph) {
}
});
} catch (TimeoutException e) {
//we cannot be here
}
model.refreshParagraphs();
addMessage(appConstants.validatorDone(),true);
}
public class ExtendedPathFinderErrorListener extends StandardPathFinderErrorListener {
public void objectCannotBeFound(ObjectBean object) {
addError(appMessages.validationObjectCannotBeGot(object.getName()),object);
}
public void objectCannotBeUsed(ObjectBean object) {
addWarning(appMessages.validationObjectIsNotUsed(object.getName()),object);
}
public void unriachebleParagraph(Paragraph paragraph) {
addError(appMessages.validationParagraphCannotBeRiached(paragraph.getName()),paragraph);
}
public void unusedParagraphConnection(ParagraphConnection connection) {
addError(appMessages.validationConnectionCannotBeUsed(connection.getFrom().getName(),connection.getTo().getName()),connection);
}
public void unusedModificator(Modificator modificator) {
addError(appMessages.unusedModificator(modificator.getName()));
}
public void unusedParameter(Parameter parameter) {
addError(appMessages.unusedParameter(parameter.getName()));
}
public void NPCIsUsedNowhere(NPC npc) {
addError(appMessages.NPCIsUsedNowhere(npc.getName()));
}
public void alchemyIsUsedNowhere(Alchemy alchemy) {
addError(appMessages.alchemyIsUsedNowhere(alchemy.getName()));
}
public void battleIsUsedNowhere(Battle battle) {
addError(appMessages.battleIsUsedNowhere(battle.getName()));
}
public boolean canBePassed(ParagraphConnection connection, Paragraph paragraph) {
return true;
}
public void noWayToSuccess(Paragraph paragraph) {
addError(appMessages.validationParagraphSuccessUnavailable(paragraph.getName()),paragraph);
}
}
public abstract class StandardPathFinderErrorListener implements PathFinderErrorListener {
final long end = new Date().getTime()+18000;
HashSet<ObjectBean> reFoundObjects = new HashSet<ObjectBean>();
public void alreadyHaveThatObject(Paragraph paragraph,ObjectBean bean) {
if (bean.isUncountable()==false) {
if (reFoundObjects.contains(bean)==false) {
reFoundObjects.add(bean);
addError(appMessages.validationObjectCanBeFoundSomeTimes(paragraph.getName(),bean.getName()),paragraph);
}
}
}
public void bothDirConnectionHasObject(ParagraphConnection connection) {
addError(appMessages.validationTwoWayConnectionHasCondition(connection.getFrom().getName(),connection.getTo().getName(),connection.getObject().getName()),connection);
}
public void duplicateConnectionBetweenParagraphs(ParagraphConnection connection) {
addError(appMessages.validationConnectionIsDuplicated(connection.getFrom().getName(),connection.getTo().getName()),connection);
}
public void noWayFromNormalParagraph(Paragraph paragraph) {
addError(appMessages.validationParagraphNoOutputConnections(paragraph.getName()),paragraph);
}
public void outwayFromFialOrSuccessParagraph(Paragraph paragraph) {
addError(appMessages.validationParagraphHasOutputConnections(paragraph.getName()),paragraph);
}
public void startFromFialOrSuccessParagraph(Paragraph paragraph) {
addError(appMessages.validationParagraphCannotBeStart(paragraph.getName()),paragraph);
}
public void uselessObjectInFailOrSuccess(Paragraph paragraph) {
addError(appMessages.validationParagraphCannotHaveObject(paragraph.getName()),paragraph);
}
public void startLocationIsNotDefined() {
addError(appConstants.validatorStartParagraphIsNotSet());
}
public void startHasIncomeConection(Paragraph paragraph) {
addError(appConstants.validatorStartParagraphHasIncomeConnections(),paragraph);
}
public boolean checkTimeout() {
//not time-out on client side
return false;
}
public void conditionalChain(ParagraphConnection connection) {
addError(appMessages.validatorConditionChainDetected(connection.getFrom().getName(),connection.getTo().getName()),connection);
}
public void gotAndLostObjectInTheSameParagraph(Paragraph paragraph, ObjectBean bean) {
addError(appMessages.validatorObjectIsLostAndFoundInTheSamePlace(paragraph.getName(),bean.getName()),paragraph);
}
public void twoInputConnectionsWithTheSameObject(Paragraph paragraph, ObjectBean object) {
addError(appMessages.twoInputConnectionsWithTheSameObject(paragraph.getName(),object.getName()),paragraph);
}
public void twoOutputConnectionsWithTheSameObject(Paragraph paragraph, ObjectBean object) {
addError(appMessages.twoOutputConnectionsWithTheSameObject(paragraph.getName(),object.getName()),paragraph);
}
public void modificatorIsSetNowhere(Modificator modificator) {
addError(appMessages.modificatorIsSetNowhere(modificator.getName()));
}
public void modificatorNotSetInConnection(ParagraphConnection connection) {
addError(appMessages.modificatorNotSetInConnection(connection.getFrom().getName(),connection.getTo().getName()),connection);
}
public void modificatorsInFialOrSuccessParagraph(Paragraph paragraph) {
addError(appMessages.modificatorsInFialOrSuccessParagraph(paragraph.getName()),paragraph);
}
public void parameterNotSetInConnection(ParagraphConnection connection) {
addError(appMessages.parameterNotSetInConnection(connection.getFrom().getName(),connection.getTo().getName()),connection);
}
public void parametersInFromFialOrSuccessParagraph(Paragraph paragraph) {
addError(appMessages.modificatorsInFialOrSuccessParagraph(paragraph.getName()),paragraph);
}
public void noSuccessParagraphs() {
addError(appConstants.noSuccessParagraphsDefined());
}
public void updateStatus(int paragraphs, int connections) {
MaskPanel.setText(appMessages.validationStatus(paragraphs,connections));
}
int check=100;
public boolean canContinue() {
boolean can = MaskPanel.isShown();
if (--check==0) {
check = 100;
if (new Date().getTime()>end) {
MaskPanel.hide();
return false;
}
}
return can;
}
public void mustGoAndNormaConnectionsInParagraph(Paragraph paragraph) {
addError(appMessages.mustGoAndNormaConnectionsInParagraph(paragraph.getName()),paragraph);
}
public void done() {
}
}
public void testConnections() {
startProgress();
DeferredCommand.addCommand(new Command() {
public void execute() {
validateConnections();
stopProgress();
}
});
}
private void validateConnections() {
addMessage(appConstants.validateConnectionsStart(),false);
ArrayList<ParagraphConnection> connections = model.getParagraphConnections();
HashMap<Paragraph,ArrayList<ParagraphConnection>> list = new HashMap<Paragraph, ArrayList<ParagraphConnection>>(model.getParagraphs().size());
//collect outcome connections
for (ParagraphConnection connection : connections) {
ArrayList<ParagraphConnection> data = list.get(connection.getFrom());
if (data==null) {
data = new ArrayList<ParagraphConnection>();
list.put(connection.getFrom(),data);
}
data.add(connection);
if (connection.isBothDirections()) {
data = list.get(connection.getTo());
if (data==null) {
data = new ArrayList<ParagraphConnection>();
list.put(connection.getTo(),data);
}
data.add(connection);
}
}
//scan connections
main_connection:
for (Iterator<Paragraph> iterator = list.keySet().iterator(); iterator.hasNext();) {
Paragraph paragraph = iterator.next();
ArrayList<ParagraphConnection> data = list.get(paragraph);
ParagraphConnection passed = null;
for (Iterator<ParagraphConnection> iterator2 = data.iterator(); iterator2.hasNext();) {
ParagraphConnection connection = iterator2.next();
if (connection.isConditional()) {
if (model.getSettings().isSkipMustGoParagraphs() && connection.getStrictness()==ParagraphConnection.STRICTNESS_MUST) {
//remove all MUST paragraphs
iterator2.remove();
continue;
}
if (connection.isHiddenUsage(model.getSettings())) {
//hidden usage, does not require name
iterator2.remove();
continue;
}
// if (passed != null && passed.isReverceCondition(connection)) {
// //remove both
// if (data.size()==2) {
// //just 2 connection with reverce condition - no checks'
// continue main_connection;
// }
// }
// passed = connection;
}
}
if (data.size()<2) {
//one or zero - no reason to check connection names
continue;
}
//all connections in list must have names
Iterator<ParagraphConnection> iterator2 = data.iterator();
while (iterator2.hasNext()) {
ParagraphConnection connection = iterator2.next();
if (emptyConnectionName(connection.getNameFrom())) {
addError(appMessages.validateConnectionEmptyNameFrom(connection.getFrom().getName(),connection.getTo().getName()),connection);
}
if (connection.isBothDirections()) {
if (emptyConnectionName(connection.getNameTo())) {
addError(appMessages.validateConnectionEmptyNameTO(connection.getFrom().getName(),connection.getTo().getName()),connection);
}
}
}
}
addMessage(appConstants.validatorDone(),true);
}
private boolean emptyConnectionName(String name) {
return name==null || name.trim().length()==0;
}
}