org.javabuilders.layout.LayoutConstraints Maven / Gradle / Ivy
The newest version!
package org.javabuilders.layout;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.javabuilders.BuilderUtils;
import org.jvyaml.YAML;
/**
* Class representing parsed layout constraints (from the "layout" node on the container)
* @author Jacek Furmankiewicz
*/
public class LayoutConstraints {
private final static Logger logger = Logger.getLogger(LayoutConstraints.class.getSimpleName());
private String layoutConstraints ="";
private List rowConstraints= new ArrayList();
private List columnConstraints= new ArrayList();
private Set cells = new LinkedHashSet();
private Map> sizeGroups = new HashMap>();
private Map additionalControlConstraints = new HashMap();
/**
* Constructor
*/
public LayoutConstraints() {}
/**
* Constructor
* @param layoutConstraints General layout constraints
*/
public LayoutConstraints(String layoutConstraints) {
this.layoutConstraints = layoutConstraints;
}
/**
* @return the layoutConstraints
*/
public String getLayoutConstraints() {
return layoutConstraints;
}
/**
* @param layoutConstraints the layoutConstraints to set
*/
public void setLayoutConstraints(String layoutConstraints) {
this.layoutConstraints = layoutConstraints;
}
/**
* @return the rowConstraints
*/
public List getRowConstraints() {
return rowConstraints;
}
/**
* String representation
*/
@Override
public String toString() {
return String.format("General: %s\nRow: %s\nColumn: %s\nCells:\n%s",
layoutConstraints,rowConstraints,columnConstraints,cells);
}
/**
* Hash code
*/
@Override
public int hashCode() {
return toString().hashCode();
}
/**
* @return the columnConstraints
*/
public List getColumnConstraints() {
return columnConstraints;
}
/**
* @return Cells
*/
public Set getCells() {
return cells;
}
/**
* Returns the cell at the specified row/column index, or null if not found
* @param rowIndex Row index
* @param columnIndex Column index
* @return Layout cell or null
*/
public LayoutCell getCellAt(int rowIndex, int columnIndex) {
for(LayoutCell cell : getCells()) {
if (cell.getRowIndex() == rowIndex && cell.getColumnIndex() == columnIndex) {
return cell;
}
}
return null;
}
/**
* @return The map of size groups (i.e. lists of controls that were flagged to be of the same size)
*/
public Map> getSizeGroups() {
return sizeGroups;
}
/**
* Returns the number of rows
* @return Row count
*/
public int getRowCount() {
int maxRow = 0;
for (LayoutCell cell : getCells()) {
if (cell.getRowIndex() > maxRow) {
maxRow = cell.getRowIndex();
}
}
return maxRow + 1;
}
/**
* Returns the number of columns
* @return Column count
*/
public int getColumnCount() {
int maxColumn = 0;
for (LayoutCell cell : getCells()) {
if (cell.getColumnIndex() > maxColumn) {
maxColumn = cell.getColumnIndex();
}
}
return maxColumn + 1;
}
/**
* For any cells flagged to do max spanning across an entire row or column,
* updates the actual HSpan/VSpan to reflect the number of rows/columns involved
*/
private void updateSpanForMaxValues() {
int rows = getRowCount();
int columns = getColumnCount();
for (LayoutCell cell : getCells()) {
if (cell.getControls().size() > 0) {
ControlConstraint co = cell.getControls().get(0);
if (co.isMaxHSpan()) {
co.setHSpan(columns - cell.getColumnIndex());
}
if (co.isMaxVSpan()) {
co.setVSpan(rows - cell.getRowIndex());
}
}
}
}
/*
* STATIC UTILITY METHODS
*/
/**
* General parser that creates the layout constraint definitions
*/
public static LayoutConstraints getParsedLayoutConstraints(String layout, String defaultRowContraint,
String defaultColumnConstraint) throws LayoutException {
LayoutConstraints constraints = new LayoutConstraints();
String columnConstraintLine = "";
//does the container have a layout node?
String[] lines = layout.split("\n");
if (lines.length > 0) {
int firstControlLine = -1;
for(int row = 0; row < lines.length; row++) {
String line = lines[row];
String trimLine = line.trim();
//figure out what type of line are we dealing with here
if (trimLine.length() > 0) {
if ('[' == trimLine.charAt(0)) {
if (trimLine.length() > 1) {
if (trimLine.startsWith("[[") && trimLine.endsWith("]]")) {
//general layout constraints [[ ]]
constraints.setLayoutConstraints(trimLine.substring(2,trimLine.length() - 2));
} else if (trimLine.startsWith("[") && trimLine.endsWith("]")) {
//accumulate all the column constraint lines
columnConstraintLine = line;
} else {
throw new LayoutException("Incorrectly formatted constraint line: " + trimLine);
}
} else {
throw new LayoutException("Incorrectly formatted constraint line: " + trimLine);
}
} else if ('{' == trimLine.charAt(0)) {
//additional control constraint line
handleAdditionalControlConstraintLine(constraints, trimLine);
} else {
if (firstControlLine == -1) {
firstControlLine = row;
}
handleControlLine(constraints, row - firstControlLine, line, defaultRowContraint);
}
}
}
}
//now that we have processed all the lines, properly run through the column constraints
if (columnConstraintLine.length() > 0) {
handleColumnConstraintsLine(constraints, columnConstraintLine, defaultColumnConstraint);
}
//rearrange all the cell numbers to be properly ordered
if (constraints.getCells().size() > 0) {
Set columns = new TreeSet();
for(LayoutCell cell : constraints.getCells()) {
columns.add(cell.getColumnIndex());
}
int column = 0;
for(Iterator it = columns.iterator(); it.hasNext(); ) {
Integer originalColumn = it.next();
//find all cells with this original column and update it to the real one
for(LayoutCell cell : constraints.getCells()) {
if (cell.getColumnIndex() == originalColumn.intValue()) {
cell.setColumnIndex(column);
}
}
column++;
}
}
constraints.updateSpanForMaxValues();
//handle the special scenario where we are dealing with vertical cell spanning
//the controls in the cells that are spanned need to be all added into the "first" original cell
for(LayoutCell cell : constraints.getCells()) {
if (cell.getHSpan() > 1 || cell.getVSpan() > 1) {
for (int rowSpan = 0; rowSpan < cell.getVSpan(); rowSpan++) {
for(int cellSpan = 0; cellSpan < cell.getHSpan(); cellSpan++) {
if (!(rowSpan == 0 && cellSpan == 0)) {
LayoutCell lowerCell = constraints.getCellAt(cell.getRowIndex() + rowSpan, cell.getColumnIndex() + cellSpan);
if (lowerCell != null) {
//move all the controls from this cell to the proper one above it
for(ControlConstraint cc : lowerCell.getControls()) {
cell.getControls().add(cc);
if (logger.isLoggable(Level.FINE)) {
logger.fine("Moved control " + cc.getControlName() + " from cell " +
lowerCell.getColumnIndex() + " " + lowerCell.getRowIndex() +
" to cell " + cell.getColumnIndex() + " " + cell.getRowIndex());
}
}
//lower cell needs to be removed altogether from the layout constraints, now that it has been cannibalized
lowerCell.getControls().clear();
}
}
}
}
//change the flow to vertical if it spans 1 column across multiple rows
if (cell.getHSpan() == 1 && cell.getVSpan() > 1) {
cell.setFlow(Flow.VERTICAL);
}
}
}
constraints.updateSpanForMaxValues(); //run it once again once all the controls have been moved around
//remove cells that have no controls in them
List tobeRemoved = new ArrayList();
for(LayoutCell cell : constraints.getCells()) {
if (cell.getControls().size() == 0) {
tobeRemoved.add(cell);
}
}
//need to be removed separately in order to avoid ConcurrentModificationException (Issue 23)
for(LayoutCell cell : tobeRemoved) {
constraints.getCells().remove(cell);
}
return constraints;
}
//handles a layout lines with controls on it
private static void handleControlLine(LayoutConstraints constraints, int row, String line,
String defaultRowConstraint) throws LayoutException {
int nextSpace = -1;
String controlData = null;
char character = ' ';
String[] controls = null;
line = line + " "; //always need one space at end to make parsing simpler
boolean rowConstraintFound = false;
for(int column = 0; column < line.length(); column++) {
character = line.charAt(column);
if (character == '[') {
//beginning of row constraints...skip rest of line
constraints.getRowConstraints().add(line.substring(column).trim());
rowConstraintFound = true;
break;
} else if (character !=' ') {
//not a space - we're in a control constraint
nextSpace = line.indexOf(" ", column);
controlData = line.substring(column,nextSpace);
//multiple controls may be in the same cell, comma separated
controls = controlData.split(",");
LayoutCell cell = new LayoutCell(row,column);
for(String control : controls) {
ControlConstraint constraint = new ControlConstraint(control);
cell.getControls().add(constraint);
}
constraints.getCells().add(cell);
column = nextSpace;
}
}
if (!rowConstraintFound) {
constraints.getRowConstraints().add(defaultRowConstraint);
}
}
private static void handleColumnConstraintsLine(LayoutConstraints co, String line, String defaultColumnConstraint) {
Map columnConstraints = new TreeMap();
//find all unique column indexes
for(LayoutCell cell : co.getCells()) {
//if (!columnConstraints.containsKey(cell.getColumnIndex()) && line.length() > cell.getColumnIndex()) {
if (line.length() > cell.getColumnIndex()) {
char charForCol = line.charAt(cell.getColumnIndex());
if (charForCol == '[') {
//OK, we have a column constraint defined
int nextColCo = line.indexOf("[", cell.getColumnIndex() + 1);
if (nextColCo == -1) {
columnConstraints.put(cell.getColumnIndex(), line.substring(cell.getColumnIndex()).trim());
} else {
columnConstraints.put(cell.getColumnIndex(), line.substring(cell.getColumnIndex(),nextColCo));
}
} else {
//no column constraint defined, put in the default one
columnConstraints.put(cell.getColumnIndex(), defaultColumnConstraint);
}
}
}
//apply all
for(String colCo : columnConstraints.values()) {
co.getColumnConstraints().add(colCo);
}
}
//handles the optional lines at the end in { name : constraint, name2: constraint2 } format
@SuppressWarnings("unchecked")
private static void handleAdditionalControlConstraintLine(LayoutConstraints co, String line) {
Map constraints = (Map)YAML.load(line);
for(String name : constraints.keySet()) {
List