/*
* Copyright (c) 2005 Matthew Hall and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Matthew Hall - initial API and implementation
*/
package org.eclipse.nebula.paperclips.core.grid;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.nebula.paperclips.core.PaperClips;
import org.eclipse.nebula.paperclips.core.internal.util.PaperClipsUtil;
import org.eclipse.nebula.paperclips.core.internal.util.Util;
import org.eclipse.swt.SWT;
/**
* Describes the properties of a column in a GridPrint.
*
* @author Matthew Hall
*/
public class GridColumn {
/**
* The default alignment used when alignment is not specified. Value is
* SWT.LEFT.
*/
public static final int DEFAULT_ALIGN = SWT.LEFT;
/**
* The default size used when size is not specified. Value is SWT.DEFAULT.
*/
public static final int DEFAULT_SIZE = SWT.DEFAULT;
/**
* The default weight used when weight is not specified. Value is 0.
*/
public static final int DEFAULT_WEIGHT = 0;
/**
* The size property for this GridColumn. Possible values:
* <ul>
* <li>GridPrint.PREFERRED - indicates that the column should be as wide as
* the preferred width of its widest element.
* <li>SWT.DEFAULT - Similar to GridPrint.PREFERRED, except that the column
* may shrink down to its minimum width if space is scarce.
* <li>A value > 0 indicates that the column should be <code>size</code>
* points wide (72pts = 1").
* </ul>
*/
public final int size;
/**
* The default alignment for Prints in this column. Possible values are
* SWT.LEFT, SWT.CENTER, SWT.RIGHT, or SWT.DEFAULT. Note that alignment
* affects the placement of PrintPieces within the grid's cell--the
* alignment elements of the PrintPiece themselves are not affected. Thus,
* in order to achieve the desired effect, a Print having an alignment
* property should be set to the same alignment as the grid cell it is added
* to. For example, a TextPrint in a right-aligned grid cell should be set
* to right alignment as well.
* <p>
* Cells that span multiple columns use the alignment of the left-most cell
* in the cell span.
*/
public final int align;
/**
* The weight of this column. If the available print space is wider than the
* grid's preferred width, this field determines how much of that extra
* space should be given to this column. A larger weight causes the column
* to receive more of the extra width. A value of 0 indicates that the
* column should not be given any excess width.
*/
public final int weight;
/**
* Constructs a GridColumn.
*
* @param align
* The default alignment for Prints in this column.
* @param size
* The size this column should be given.
* @param weight
* The weight this column should be given.
*/
public GridColumn(int align, int size, int weight) {
this.align = checkAlign(align);
this.size = checkSize(size);
this.weight = checkWeight(weight);
}
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + align;
result = prime * result + size;
result = prime * result + weight;
return result;
}
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
GridColumn other = (GridColumn) obj;
if (align != other.align)
return false;
if (size != other.size)
return false;
if (weight != other.weight)
return false;
return true;
}
private static int checkAlign(int align) {
align = PaperClipsUtil.firstMatch(align, new int[] { SWT.LEFT,
SWT.CENTER, SWT.RIGHT, SWT.DEFAULT }, 0);
if (align == 0)
PaperClips
.error(
SWT.ERROR_INVALID_ARGUMENT,
"Alignment argument must be one of SWT.LEFT, SWT.CENTER, SWT.RIGHT, or SWT.DEFAULT"); //$NON-NLS-1$
if (align == SWT.DEFAULT)
return DEFAULT_ALIGN;
return align;
}
private static int checkSize(int size) {
if (size != SWT.DEFAULT && size != GridPrint.PREFERRED && size <= 0)
PaperClips
.error(SWT.ERROR_INVALID_ARGUMENT,
"Size argument must be SWT.DEFAULT, GridPrint.PREFERRED, or > 0"); //$NON-NLS-1$
return size;
}
private static int checkWeight(int grow) {
if (grow < 0)
PaperClips.error(SWT.ERROR_INVALID_ARGUMENT,
"Weight argument must be >= 0"); //$NON-NLS-1$
return grow;
}
/**
* Parses the given column spec and returns a GridColumn matching that spec.
* <p>
* Format:
*
* <pre>
* [align:]size[:grow]
*
* align = L | LEFT |
* C | CENTER |
* R | RIGHT
* size = P | PREF | PREFERRED |
* D | DEF | DEFAULT |
* (Positive number)[PT|IN|INCH|CM|MM]
* weight = N | NONE |
* G | GROW | G(#) | GROW(#)
* </pre>
*
* The default alignment is LEFT. The
*
* <code>weight</code> argument expresses the weight property: NONE
* indicates a weight of 0; GROW indicates a weight of 1; and GROW(3)
* indicates a weight of 3. The default weight (if <code>weight</code> is
* omitted) is 0.
* <p>
* Examples:
*
* <pre>
* LEFT:DEFAULT:GROW // left-aligned, default size, weight=1
* R:72PT:N // light-aligned, 72 points (1") wide, weight=0
* right:72 // identical to previous line
* c:pref:none // center-aligned, preferred size, weight=0
* p // left-aligned (default), preferred size, weight=0
* r:2inch // right-aligned, 2 inches (50.8mm)
* r:50.8mm // right-aligned, 50.8 mm (2")
* </pre>
*
* @param spec
* the column spec that will be parsed.
* @return a GridColumn matching the column spec.
* @see #align
* @see #size
* @see #weight
*/
public static GridColumn parse(String spec) {
Util.notNull(spec);
String[] matches = spec.split("\\s*:\\s*"); //$NON-NLS-1$
if (matches.length == 0)
PaperClips.error(SWT.ERROR_INVALID_ARGUMENT, "Missing column spec"); //$NON-NLS-1$
int align = DEFAULT_ALIGN;
int size = DEFAULT_SIZE;
int grow = DEFAULT_WEIGHT;
if (matches.length == 1) {
// One option: must be size
size = parseSize(matches[0]);
} else if (matches.length == 2) {
// Two possible scenarios:
// 1. align:size
// 2. size:weight
if (isAlign(matches[0])) {
align = parseAlign(matches[0]);
size = parseSize(matches[1]);
} else {
size = parseSize(matches[0]);
grow = parseWeight(matches[1]);
}
} else if (matches.length == 3) {
align = parseAlign(matches[0]);
size = parseSize(matches[1]);
grow = parseWeight(matches[2]);
}
return new GridColumn(align, size, grow);
}
// Alignment patterns
private static final Pattern LEFT_ALIGN_PATTERN = Pattern.compile(
"^l(eft)?$", //$NON-NLS-1$
Pattern.CASE_INSENSITIVE);
private static final Pattern CENTER_ALIGN_PATTERN = Pattern.compile(
"^c(enter)?$", //$NON-NLS-1$
Pattern.CASE_INSENSITIVE);
private static final Pattern RIGHT_ALIGN_PATTERN = Pattern.compile(
"^r(ight)?$", //$NON-NLS-1$
Pattern.CASE_INSENSITIVE);
private static final Pattern ANY_ALIGN_PATTERN = Pattern.compile(
"^l(eft)?|c(enter)?|r(ight)?$", //$NON-NLS-1$
Pattern.CASE_INSENSITIVE);
private static boolean isAlign(String alignmentString) {
return ANY_ALIGN_PATTERN.matcher(alignmentString).matches();
}
private static int parseAlign(String alignmentString) {
if (LEFT_ALIGN_PATTERN.matcher(alignmentString).matches())
return SWT.LEFT;
else if (CENTER_ALIGN_PATTERN.matcher(alignmentString).matches())
return SWT.CENTER;
else if (RIGHT_ALIGN_PATTERN.matcher(alignmentString).matches())
return SWT.RIGHT;
PaperClips.error(SWT.ERROR_INVALID_ARGUMENT,
"Unknown alignment \"" + alignmentString + "\""); //$NON-NLS-1$//$NON-NLS-2$
return 0; // unreachable
}
// Size patterns.
private static final Pattern DEFAULT_SIZE_PATTERN = Pattern.compile(
"^d(ef(ault)?)?$", //$NON-NLS-1$
Pattern.CASE_INSENSITIVE);
private static final Pattern PREFERRED_SIZE_PATTERN = Pattern.compile(
"^p(ref(erred)?)?", //$NON-NLS-1$
Pattern.CASE_INSENSITIVE);
private static final Pattern EXPLICIT_SIZE_PATTERN = Pattern.compile(
"^(\\d+(\\.\\d+)?)\\s*(pt|in(ch)?|mm|cm)?$", //$NON-NLS-1$
Pattern.CASE_INSENSITIVE);
private static int parseSize(String sizeString) {
Matcher matcher;
if (DEFAULT_SIZE_PATTERN.matcher(sizeString).matches())
return SWT.DEFAULT;
else if (PREFERRED_SIZE_PATTERN.matcher(sizeString).matches())
return GridPrint.PREFERRED;
else if ((matcher = EXPLICIT_SIZE_PATTERN.matcher(sizeString))
.matches()) {
return (int) Math.ceil(convertToPoints(Double.parseDouble(matcher
.group(1)), matcher.group(3)));
} else {
PaperClips.error(SWT.ERROR_INVALID_ARGUMENT,
"Unknown size pattern: \"" + sizeString + "\""); //$NON-NLS-1$ //$NON-NLS-2$
return 0; // unreachable
}
}
private static double convertToPoints(double value, String unit) {
if (unit == null || unit.length() == 0 || unit.equalsIgnoreCase("pt")) //$NON-NLS-1$
return value;
else if (unit.equalsIgnoreCase("in") || unit.equalsIgnoreCase("inch")) //$NON-NLS-1$ //$NON-NLS-2$
return 72 * value;
else if (unit.equalsIgnoreCase("cm")) //$NON-NLS-1$
return 72 * value / 2.54;
else if (unit.equalsIgnoreCase("mm")) //$NON-NLS-1$
return 72 * value / 25.4;
PaperClips.error(SWT.ERROR_INVALID_ARGUMENT,
"Unknown unit \"" + unit + "\"."); //$NON-NLS-1$ //$NON-NLS-2$
return 0;
}
private static final Pattern WEIGHTLESS_PATTERN = Pattern.compile(
"n(one)?", //$NON-NLS-1$
Pattern.CASE_INSENSITIVE);
private static final Pattern WEIGHTED_PATTERN = Pattern.compile(
"(g(row)?)(\\((\\d+)\\))?", // yikes //$NON-NLS-1$
Pattern.CASE_INSENSITIVE);
private static int parseWeight(String weightString) {
Matcher matcher;
if (WEIGHTLESS_PATTERN.matcher(weightString).matches())
return 0;
else if ((matcher = WEIGHTED_PATTERN.matcher(weightString)).matches()) {
String weight = matcher.group(4);
return (weight == null) ? 1 : Integer.parseInt(weight);
} else {
PaperClips.error(SWT.ERROR_INVALID_ARGUMENT,
"Illegal grow pattern: \"" + weightString //$NON-NLS-1$
+ "\""); //$NON-NLS-1$
return 0; // unreachable
}
}
GridColumn copy() {
return new GridColumn(align, size, weight);
}
}