Java tutorial
/* Copyright 2015 Sven van der Meer <vdmeer.sven@mykolab.com> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package de.vandermeer.asciilist; import java.util.ArrayList; import java.util.List; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.NotImplementedException; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.text.StrBuilder; import org.apache.commons.lang3.text.WordUtils; /** * Abstract implementation as base for the {@link AsciiList} hierarchy. * * @author Sven van der Meer <vdmeer.sven@mykolab.com> * @version v0.0.3 build 160301 (01-Mar-16) for Java 1.7 * @since v0.0.1 */ public abstract class AbstractAsciiList implements AsciiList { /** The items of a list. */ protected List<Object> items; /** The indentation before the label (and the pre-label string), default is 1. */ protected int preLabelIndent; /** The indentation after the label (and the post-label string), default is 1. */ protected int postLabelIndent; /** A string printed directly before the label, other than indentation, default is empty string. */ protected String preLabelStr; /** A string printed directly after the label, other than indentation, default is empty string. */ protected String postLabelStr; /** The level of the list, default is 1. */ protected int level = 1; /** Flag indicating if the list is continued from a previous list of the same type or not. */ protected final boolean isContinued; /** The maximum item indentation, 0 until calculated. */ protected int maxItemIndent = 0; /** The maximum width if the list (and all items and sub-lists), -1 for no width until set otherwise. */ protected int width = -1; /** * Creates a new list. */ public AbstractAsciiList() { this(true); } /** * Creates a new list. * @param list original list */ public AbstractAsciiList(AbstractAsciiList list) { this(list.isContinued); this.preLabelIndent = list.preLabelIndent; this.postLabelIndent = list.postLabelIndent; this.preLabelStr = list.preLabelStr; this.postLabelStr = list.postLabelStr; this.level = list.level; this.items.addAll(list.items); } /** * Creates a new list. * @param isContinued true if the list is continued from a previous list of the same type, false otherwise */ public AbstractAsciiList(boolean isContinued) { this.items = new ArrayList<>(); this.isContinued = isContinued; this.setLabelDefaults(); } @Override public AsciiList setLabelDefaults() { this.preLabelIndent = 1; this.postLabelIndent = 1; this.preLabelStr = ""; this.postLabelStr = ""; return this; } @Override public int calculateMaxIndentation() { int position = 0; for (int i = 0; i < this.items.size(); i++) { if (this.items.get(i) instanceof AsciiListItem) { position++; int indent = this.calculateMaxIndentation((AsciiListItem) this.items.get(i), position); if (this.maxItemIndent < indent) { this.maxItemIndent = indent; } } } return this.maxItemIndent; } @Override public List<Object> getItems() { return this.items; } @Override public int getLevel() { return this.level; } @Override public int getPostLabelIndent() { return this.postLabelIndent; } @Override public String getPostLabelString() { return this.postLabelStr; } @Override public int getPreLabelIndent() { return this.preLabelIndent; } @Override public String getPreLabelString() { return this.preLabelStr; } @Override public boolean isContinuedList() { return this.isContinued; } @Override public String render() { this.prepareRender(); StrBuilder ret = new StrBuilder(100); this.maxItemIndent = 0; this.calculateMaxIndentation(); if (this.width > 0 && ((maxItemIndent + 6) > this.width)) { throw new IllegalArgumentException("width <" + this.width + "> is to small for list content with indentation already at <" + this.maxItemIndent + ">"); } int position = 0; for (int i = 0; i < this.items.size(); i++) { if (this.items.get(i) instanceof AsciiListItem) { position++; ret.appendSeparator("\n") .append(this.wrapItem(this.renderItem((AsciiListItem) this.items.get(i), position))); } else if (this.items.get(i) instanceof AsciiList) { AsciiList l = (AsciiList) this.items.get(i); l.setPreLabelIndent(this.maxItemIndent); ret.appendSeparator("\n").append(l.render()); } else { throw new NotImplementedException( "not yet implemented, only AsciiList and AsciiListItem supported"); } } return ret.toString(); } @Override public AsciiList setLevel(int level) { if (level > 1) { this.level = level; } return this; } @Override public void prepareRender() { for (Object obj : this.items) { if (obj instanceof AsciiList) { ((AsciiList) obj).setLevel(this.level + 1); ((AsciiList) obj).setWidth(this.width); } } } @Override public AsciiList setPostLabelIndent(int indent) { if (indent > -1) { this.postLabelIndent = indent; } return this; } @Override public AsciiList setPostLabelString(String str) { this.postLabelStr = str; return this; } @Override public AsciiList setPreLabelIndent(int indent) { if (indent > -1) { this.preLabelIndent = indent; } return this; } @Override public AsciiList setPreLabelString(String str) { this.preLabelStr = str; return this; } @Override public AsciiList setWidth(int width) { this.width = width; return this; } /** * Wraps a rendered list item for the width given in the list. * @param renderedItem the rendered item to wrap * @return a string with the wrapped item (lines separated by "\n") */ protected String wrapItem(String renderedItem) { String ret = ""; if (renderedItem.contains(IMPLICIT_NEWLINE)) { //for descriptions with multi lines String[] split = StringUtils.split(renderedItem, IMPLICIT_NEWLINE); String rest = StringUtils.repeat(" ", this.maxItemIndent) + split[1]; if (this.width > 0 && rest.length() < this.width) { return split[0] + "\n" + rest; } else if (this.width < 0) { return split[0] + "\n" + rest; } } if (this.width > 0 && renderedItem.length() > this.width) { String[] wrap = StringUtils.split(WordUtils.wrap(renderedItem, (this.width - this.preLabelIndent - this.preLabelStr.length()), IMPLICIT_NEWLINE, true), IMPLICIT_NEWLINE); ret += StringUtils.repeat(" ", this.preLabelIndent) + StringUtils.repeat(" ", this.preLabelStr.length()) + wrap[0]; if (wrap.length > 1) { ret += "\n" + this.wrapItemNextLine(StringUtils.repeat(" ", this.maxItemIndent) + StringUtils.join(ArrayUtils.remove(wrap, 0), " ")); } return ret; } return renderedItem; } /** * Wraps all following lines of an item * @param str the string to wrap * @return wrapped string */ protected String wrapItemNextLine(String str) { String[] wrap = StringUtils.split( WordUtils.wrap(str, (this.width - this.maxItemIndent), IMPLICIT_NEWLINE, true), IMPLICIT_NEWLINE); String ret = StringUtils.repeat(" ", this.maxItemIndent) + wrap[0]; if (wrap.length == 1) { return ret; } return ret + "\n" + this.wrapItemNextLine( StringUtils.repeat(" ", this.maxItemIndent) + StringUtils.join(ArrayUtils.remove(wrap, 0), " ")); } }