/* ========================================================================
 * PlantUML : a free UML diagram generator
 * ========================================================================
 *
 * (C) Copyright 2009-2017, Arnaud Roques
 *
 * Project Info:  http://plantuml.com
 * 
 * If you like this project or if you find it useful, you can support us at:
 * 
 * http://plantuml.com/patreon (only 1$ per month!)
 * http://plantuml.com/paypal
 * 
 * This file is part of PlantUML.
 *
 * PlantUML is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * PlantUML distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
 * USA.
 *
 *
 * Original Author:  Arnaud Roques
 *
 */
package net.sourceforge.plantuml;

import java.awt.Color;
import java.awt.geom.Dimension2D;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;

import net.sourceforge.plantuml.api.ImageDataSimple;
import net.sourceforge.plantuml.asciiart.UmlCharArea;
import net.sourceforge.plantuml.core.DiagramDescription;
import net.sourceforge.plantuml.core.ImageData;
import net.sourceforge.plantuml.core.UmlSource;
import net.sourceforge.plantuml.eggs.PSystemWelcome;
import net.sourceforge.plantuml.graphic.GraphicPosition;
import net.sourceforge.plantuml.graphic.GraphicStrings;
import net.sourceforge.plantuml.graphic.HorizontalAlignment;
import net.sourceforge.plantuml.graphic.HtmlColor;
import net.sourceforge.plantuml.graphic.HtmlColorSimple;
import net.sourceforge.plantuml.graphic.InnerStrategy;
import net.sourceforge.plantuml.graphic.StringBounder;
import net.sourceforge.plantuml.graphic.TextBlock;
import net.sourceforge.plantuml.graphic.TextBlockUtils;
import net.sourceforge.plantuml.svek.TextBlockBackcolored;
import net.sourceforge.plantuml.ugraphic.ColorMapperIdentity;
import net.sourceforge.plantuml.ugraphic.ImageBuilder;
import net.sourceforge.plantuml.ugraphic.MinMax;
import net.sourceforge.plantuml.ugraphic.UGraphic;
import net.sourceforge.plantuml.ugraphic.UImage;
import net.sourceforge.plantuml.ugraphic.UTranslate;
import net.sourceforge.plantuml.ugraphic.txt.UGraphicTxt;
import net.sourceforge.plantuml.version.PSystemVersion;

public class PSystemError extends AbstractPSystem {

	private final LineLocation higherErrorPosition;
	private final List printedErrors;
	private final List debugLines = new ArrayList();

	public PSystemError(UmlSource source, ErrorUml singleError, List debugLines) {
		this(source, Collections.singletonList(singleError), debugLines);
	}

	private PSystemError(UmlSource source, List all, List debugLines) {
		this.setSource(source);

		final LineLocation execution = getHigherErrorPosition2(ErrorUmlType.EXECUTION_ERROR, all);
		final LineLocation syntax = getHigherErrorPosition2(ErrorUmlType.SYNTAX_ERROR, all);

		if (execution == null && syntax == null) {
			throw new IllegalStateException();
		}

		if (execution != null && (syntax == null || execution.compareTo(syntax) >= 0)) {
			higherErrorPosition = execution;
			printedErrors = getErrorsAt2(execution, ErrorUmlType.EXECUTION_ERROR, all);
		} else {
			// assert syntax.compareTo(execution) > 0;
			higherErrorPosition = syntax;
			printedErrors = getErrorsAt2(syntax, ErrorUmlType.SYNTAX_ERROR, all);
		}

		if (debugLines != null) {
			this.debugLines.addAll(debugLines);
		}

	}

	private String getSuggestColor(boolean useRed) {
		if (useRed) {
			return "black";
		}
		return "white";
	}

	private String getRed(boolean useRed) {
		if (useRed) {
			return "#CD0A0A";
		}
		return "red";
	}

	@Override
	final protected ImageData exportDiagramNow(OutputStream os, int num, FileFormatOption fileFormat, long seed)
			throws IOException {
		if (fileFormat.getFileFormat() == FileFormat.ATXT || fileFormat.getFileFormat() == FileFormat.UTXT) {
			final UGraphicTxt ugt = new UGraphicTxt();
			final UmlCharArea area = ugt.getCharArea();
			area.drawStringsLR(getTextStrings(), 0, 0);
			area.print(new PrintStream(os));
			return new ImageDataSimple(1, 1);

		}
		final boolean useRed = fileFormat.isUseRedForError();
		final TextBlockBackcolored result = GraphicStrings.createForError(getHtmlStrings(useRed), useRed);

		TextBlock udrawable;
		final ImageBuilder imageBuilder = new ImageBuilder(new ColorMapperIdentity(), 1.0, result.getBackcolor(),
				getMetadata(), null, 0, 0, null, false);
		if (getSource().getTotalLineCount() < 5) {
			udrawable = addWelcome(result);
		} else {
			udrawable = result;
		}
		final int min = (int) (System.currentTimeMillis() / 60000L) % 60;
		if (min == 0) {
			udrawable = addMessage(udrawable);
		}
		imageBuilder.setUDrawable(udrawable);
		return imageBuilder.writeImageTOBEMOVED(fileFormat, seed(), os);
	}

	private TextBlockBackcolored getWelcome() throws IOException {
		return new PSystemWelcome(GraphicPosition.BACKGROUND_CORNER_TOP_RIGHT).getGraphicStrings();
	}

	private TextBlock addWelcome(final TextBlockBackcolored result) throws IOException {
		final TextBlockBackcolored welcome = getWelcome();
		return TextBlockUtils.mergeTB(welcome, result, HorizontalAlignment.LEFT);
	}

	private TextBlock addMessage(final TextBlock source) throws IOException {
		final TextBlock message = getMessage();
		TextBlock result = TextBlockUtils.mergeTB(message, source, HorizontalAlignment.LEFT);
		result = TextBlockUtils.mergeTB(result, message, HorizontalAlignment.LEFT);
		return result;
	}

	private TextBlockBackcolored getMessage() {
		final UImage message = new UImage(PSystemVersion.getTime());
		final HtmlColor backImage = new HtmlColorSimple(new Color(message.getImage().getRGB(0, 0)), false);
		final double imWidth = message.getWidth();
		final double imHeight = message.getHeight();
		return new TextBlockBackcolored() {

			public void drawU(UGraphic ug) {
				ug.apply(new UTranslate(1, 1)).draw(message);
			}

			public Rectangle2D getInnerPosition(String member, StringBounder stringBounder, InnerStrategy strategy) {
				return null;
			}

			public Dimension2D calculateDimension(StringBounder stringBounder) {
				return new Dimension2DDouble(imWidth + 1, imHeight + 1);
			}

			public MinMax getMinMax(StringBounder stringBounder) {
				return MinMax.fromMax(imWidth + 1, imHeight + 1);
			}

			public HtmlColor getBackcolor() {
				return backImage;
			}
		};

	}

	private List getTextStrings() {
		final List result = new ArrayList(getStack());
		if (result.size() > 0) {
			result.add(" ");
		}

		// final int limit = 4;
		// int start;
		// final int skip = higherErrorPosition - limit + 1;
		// if (skip <= 0) {
		// start = 0;
		// } else {
		// if (skip == 1) {
		// result.add("... (skipping 1 line) ...");
		// } else {
		// result.add("... (skipping " + skip + " lines) ...");
		// }
		// start = higherErrorPosition - limit + 1;
		// }
		// for (int i = start; i < higherErrorPosition; i++) {
		// result.add(getSource().getLine(i));
		// }
		for (String s : getPartialCode()) {
			result.add(s);
		}
		final String errorLine = getSource().getLine(higherErrorPosition);
		final String err = StringUtils.hideComparatorCharacters(errorLine);
		if (StringUtils.isNotEmpty(err)) {
			result.add(err);
		}
		final StringBuilder underscore = new StringBuilder();
		for (int i = 0; i < errorLine.length(); i++) {
			underscore.append("^");
		}
		result.add(underscore.toString());
		final Collection textErrors = new LinkedHashSet();
		for (ErrorUml er : printedErrors) {
			textErrors.add(er.getError());
		}
		for (String er : textErrors) {
			result.add(" " + er);
		}
		boolean first = true;
		for (String s : getSuggest()) {
			if (first) {
				result.add(" " + s);
			} else {
				result.add(s);
			}
			first = false;
		}
		result.addAll(this.debugLines);

		return result;
	}

	private List getStack() {
		LineLocation lineLocation = getLineLocation();
		final List result = new ArrayList();
		if (lineLocation != null) {
			append(result, lineLocation);
			while (lineLocation.getParent() != null) {
				lineLocation = lineLocation.getParent();
				append(result, lineLocation);
			}
		}
		return result;
	}

	public LineLocation getLineLocation() {
		for (ErrorUml err : printedErrors) {
			if (err.getLineLocation() != null) {
				return err.getLineLocation();
			}
		}
		return null;
	}

	private void append(List result, LineLocation lineLocation) {
		if (lineLocation.getDescription() != null) {
			result.add("[From " + lineLocation.getDescription() + " (line " + (lineLocation.getPosition() + 1) + ") ]");
		}
	}

	private List getPartialCode() {
		List result = new ArrayList();
		for (Iterator it = getSource().iterator2(); it.hasNext();) {
			final CharSequence2 s = it.next();
			if (s.getLocation().compareTo(higherErrorPosition) < 0) {
				result.add(s.toString());
			}
		}
		final int limit = 4;
		if (result.size() > limit) {
			final int skip = result.size() - limit + 1;
			final String skipMessage;
			if (skip == 1) {
				skipMessage = "... (skipping 1 line) ...";
			} else {
				skipMessage = "... (skipping " + skip + " lines) ...";
			}
			result = new ArrayList(result.subList(skip, result.size()));
			result.add(0, skipMessage);
		}
		return result;

	}

	private List getHtmlStrings(boolean useRed) {
		final List htmlStrings = new ArrayList(getStack());
		if (htmlStrings.size() > 0) {
			htmlStrings.add("----");
		}

		// final int limit = 4;
		// int start;
		// final int skip = higherErrorPosition - limit + 1;
		// if (skip <= 0) {
		// start = 0;
		// } else {
		// if (skip == 1) {
		// htmlStrings.add("... (skipping 1 line) ...");
		// } else {
		// htmlStrings.add("... (skipping " + skip + " lines) ...");
		// }
		// start = higherErrorPosition - limit + 1;
		// }
		// for (int i = start; i < higherErrorPosition; i++) {
		// htmlStrings.add(StringUtils.hideComparatorCharacters(getSource().getLine(i)));
		// }
		for (String s : getPartialCode()) {
			htmlStrings.add(StringUtils.hideComparatorCharacters(s));
		}
		final String errorLine = getSource().getLine(higherErrorPosition);
		final String err = StringUtils.hideComparatorCharacters(errorLine);
		if (StringUtils.isNotEmpty(err)) {
			htmlStrings.add("" + err + "");
		}
		final Collection textErrors = new LinkedHashSet();
		for (ErrorUml er : printedErrors) {
			textErrors.add(er.getError());
		}
		for (String er : textErrors) {
			htmlStrings.add(" " + er + "");
		}
		boolean first = true;
		for (String s : getSuggest()) {
			if (first) {
				htmlStrings.add(" " + s + "");
			} else {
				htmlStrings.add("" + StringUtils.hideComparatorCharacters(s)
						+ "");
			}
			first = false;
		}
		htmlStrings.addAll(this.debugLines);

		return htmlStrings;
	}

	public List getSuggest() {
		boolean suggested = false;
		for (ErrorUml er : printedErrors) {
			if (er.hasSuggest()) {
				suggested = true;
			}
		}
		if (suggested == false) {
			return Collections.emptyList();
		}
		final List result = new ArrayList();
		result.add("Did you mean:");
		for (ErrorUml er : printedErrors) {
			if (er.hasSuggest()) {
				result.add(er.getSuggest().getSuggestedLine());
			}
		}
		return Collections.unmodifiableList(result);

	}

	private Collection getErrors(ErrorUmlType type, List all) {
		final Collection result = new LinkedHashSet();
		for (ErrorUml error : all) {
			if (error.getType() == type) {
				result.add(error);
			}
		}
		return result;
	}

	// private int getHigherErrorPosition(ErrorUmlType type, List all) {
	// int max = Integer.MIN_VALUE;
	// for (ErrorUml error : getErrors(type, all)) {
	// if (error.getPosition() > max) {
	// max = error.getPosition();
	// }
	// }
	// return max;
	// }

	private LineLocation getHigherErrorPosition2(ErrorUmlType type, List all) {
		LineLocation max = null;
		for (ErrorUml error : getErrors(type, all)) {
			if (max == null || error.getLineLocation().compareTo(max) > 0) {
				max = error.getLineLocation();
			}
		}
		return max;
	}

	// private List getErrorsAt(int position, ErrorUmlType type, List all) {
	// final List result = new ArrayList();
	// for (ErrorUml error : getErrors(type, all)) {
	// if (error.getPosition() == position && StringUtils.isNotEmpty(error.getError())) {
	// result.add(error);
	// }
	// }
	// return result;
	// }

	private List getErrorsAt2(LineLocation position, ErrorUmlType type, List all) {
		final List result = new ArrayList();
		for (ErrorUml error : getErrors(type, all)) {
			if (error.getLineLocation().compareTo(position) == 0 && StringUtils.isNotEmpty(error.getError())) {
				result.add(error);
			}
		}
		return result;
	}

	public DiagramDescription getDescription() {
		return new DiagramDescription("(Error)");
	}

	public final LineLocation getHigherErrorPosition2() {
		return higherErrorPosition;
	}

	public final Collection getErrorsUml() {
		return Collections.unmodifiableCollection(printedErrors);
	}

	@Override
	public String getWarningOrError() {
		final StringBuilder sb = new StringBuilder();
		sb.append(getDescription());
		sb.append(BackSlash.CHAR_NEWLINE);
		for (CharSequence t : getTitle().getDisplay()) {
			sb.append(t);
			sb.append(BackSlash.CHAR_NEWLINE);
		}
		sb.append(BackSlash.CHAR_NEWLINE);
		for (String s : getSuggest()) {
			sb.append(s);
			sb.append(BackSlash.CHAR_NEWLINE);
		}
		return sb.toString();
	}

	public static PSystemError merge(Collection ps) {
		UmlSource source = null;
		final List errors = new ArrayList();
		final List debugs = new ArrayList();
		for (PSystemError system : ps) {
			if (system == null) {
				continue;
			}
			if (system.getSource() != null && source == null) {
				source = system.getSource();
			}
			errors.addAll(system.getErrorsUml());
			debugs.addAll(system.debugLines);
			if (system.debugLines.size() > 0) {
				debugs.add("-");
			}
		}
		if (source == null) {
			throw new IllegalStateException();
		}
		return new PSystemError(source, errors, debugs);
	}

}

 

Ads help maintain this website.