blob: 06812ed84448dea6ae8e0b2c43c51fd264341e43 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
/* $Id$ */
package org.apache.fop.traits;
import java.awt.Color;
import java.io.Serializable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.fo.expr.PropertyException;
import org.apache.fop.util.ColorUtil;
/**
* Border properties.
* Class to store border trait properties for the area tree.
*/
public class BorderProps implements Serializable {
private static final long serialVersionUID = 8022237892391068187L;
public enum Mode {
SEPARATE("separate") {
@Override
int getClippedWidth(BorderProps bp) {
return 0;
}
},
COLLAPSE_INNER("collapse-inner"), // for borders inside a table
COLLAPSE_OUTER("collapse-outer"); // for borders at the table's outer border
private final String value;
Mode(String value) {
this.value = value;
}
int getClippedWidth(BorderProps bp) {
return bp.width / 2;
};
}
/** Border style (one of EN_*) */
public final int style; // Enum for border style
/** Border color */
public final Color color;
/** Border width */
public final int width;
private final int radiusStart;
private final int radiusEnd;
/** Border mode */
private final Mode mode;
/**
* Constructs a new BorderProps instance.
* @param style border style (one of EN_*)
* @param width border width
* @param radiusStart radius of start corner in the direction perpendicular to border segment
* @param radiusEnd radius of end corner in the direction perpendicular to border segment
* @param color border color
* @param mode border mode ((one of SEPARATE, COLLAPSE_INNER and COLLAPSE_OUTER)
*/
public BorderProps(int style, int width, int radiusStart, int radiusEnd, Color color, Mode mode) {
this.style = style;
this.width = width;
this.radiusStart = radiusStart;
this.radiusEnd = radiusEnd;
this.color = color;
this.mode = mode;
}
/**
* Factory method for a new BorderProps instance with rectangular corners.
* @param style border style (one of EN_*)
* @param width border width
* @param color border color
* @param mode border mode ((one of SEPARATE, COLLAPSE_INNER and COLLAPSE_OUTER)
*/
public static BorderProps makeRectangular(int style, int width, Color color, Mode mode) {
return new BorderProps(style, width, 0, 0, color, mode);
}
private BorderProps(String style, int width, int radiusStart, int radiusEnd, Color color, Mode mode) {
this(getConstantForStyle(style), width, radiusStart, radiusEnd, color, mode);
}
/**
*
* @return the radius of the corner adjacent to the before or start border
*/
public int getRadiusStart() {
return radiusStart;
}
/**
* @return the radius of the corner adjacent to the after or end border
*/
public int getRadiusEnd() {
return radiusEnd;
}
/**
* @return the border mode ((one of SEPARATE, COLLAPSE_INNER and COLLAPSE_OUTER)
*/
public Mode getMode() {
return mode;
}
/**
* @param bp the border properties or null
* @return the effective width of the clipped part of the border
*/
public static int getClippedWidth(BorderProps bp) {
return bp == null ? 0 : bp.mode.getClippedWidth(bp);
}
private String getStyleString() {
return BorderStyle.valueOf(style).getName();
}
private static int getConstantForStyle(String style) {
return BorderStyle.valueOf(style).getEnumValue();
}
public boolean isCollapseOuter() {
return mode == Mode.COLLAPSE_OUTER;
}
/** {@inheritDoc} */
@Override
public int hashCode() {
return toString().hashCode();
}
/** {@inheritDoc} */
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
} else if (obj == this) {
return true;
} else {
if (obj instanceof BorderProps) {
BorderProps other = (BorderProps) obj;
return (style == other.style)
&& org.apache.xmlgraphics.java2d.color.ColorUtil.isSameColor(
color, other.color)
&& width == other.width
&& mode == other.mode
&& radiusStart == other.radiusStart
&& radiusEnd == other.radiusEnd;
}
}
return false;
}
/**
* Returns a BorderProps represtation of a string of the format as written by
* BorderProps.toString().
* @param foUserAgent FOP user agent caching ICC profiles
* @param s the string
* @return a BorderProps instance
*/
public static BorderProps valueOf(FOUserAgent foUserAgent, String s) {
return BorderPropsDeserializer.INSTANCE.valueOf(foUserAgent, s);
}
/** {@inheritDoc} */
@Override
public String toString() {
StringBuffer sbuf = new StringBuffer();
sbuf.append('(')
.append(getStyleString()).append(',')
.append(ColorUtil.colorToString(color)).append(',')
.append(width);
if (!mode.equals(Mode.SEPARATE)) {
sbuf.append(",").append(mode.value);
}
if (radiusStart != 0 || radiusEnd != 0) {
if (mode.equals(Mode.SEPARATE)) {
// Because of the corner radii properties the mode must be set
// so that the parameter index is consistent
sbuf.append(",").append(Mode.SEPARATE.value);
}
sbuf.append(',').append(radiusStart)
.append(',').append(radiusEnd);
}
sbuf.append(')');
return sbuf.toString();
}
private static final class BorderPropsDeserializer {
private static final BorderPropsDeserializer INSTANCE = new BorderPropsDeserializer();
private static final Pattern PATTERN = Pattern.compile("([^,\\(]+(?:\\(.*\\))?)");
private BorderPropsDeserializer() {
}
public BorderProps valueOf(FOUserAgent foUserAgent, String s) {
if (s.startsWith("(") && s.endsWith(")")) {
s = s.substring(1, s.length() - 1);
Matcher m = PATTERN.matcher(s);
m.find();
String style = m.group();
m.find();
String color = m.group();
m.find();
int width = Integer.parseInt(m.group());
Mode mode = Mode.SEPARATE;
if (m.find()) {
String ms = m.group();
if (Mode.COLLAPSE_INNER.value.equalsIgnoreCase(ms)) {
mode = Mode.COLLAPSE_INNER;
} else if (Mode.COLLAPSE_OUTER.value.equalsIgnoreCase(ms)) {
mode = Mode.COLLAPSE_OUTER;
}
}
Color c;
try {
c = ColorUtil.parseColorString(foUserAgent, color);
} catch (PropertyException e) {
throw new IllegalArgumentException(e.getMessage());
}
int startRadius = 0;
int endRadius = 0;
if (m.find()) {
startRadius = Integer.parseInt(m.group());
m.find();
endRadius = Integer.parseInt(m.group());
}
return new BorderProps(style, width, startRadius, endRadius, c, mode);
} else {
throw new IllegalArgumentException("BorderProps must be surrounded by parentheses");
}
}
}
}