blob: 755d84547e53888dede1a460e0bfc2558704d90e [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.fo.properties;
import java.awt.Color;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Arrays;
import org.apache.xmlgraphics.image.loader.ImageException;
import org.apache.xmlgraphics.image.loader.ImageInfo;
import org.apache.xmlgraphics.image.loader.ImageManager;
import org.apache.xmlgraphics.image.loader.ImageSessionContext;
import org.apache.fop.ResourceEventProducer;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.datatypes.Length;
import org.apache.fop.datatypes.PercentBaseContext;
import org.apache.fop.datatypes.URISpecification;
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.FObj;
import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.expr.PropertyException;
/**
* Stores all common border and padding properties.
* See Sec. 7.7 of the XSL-FO Standard.
*/
public class CommonBorderPaddingBackground {
/**
* cache holding all canonical instances
* (w/ absolute background-position-* and padding-*)
*/
private static final PropertyCache<CommonBorderPaddingBackground> CACHE
= new PropertyCache<CommonBorderPaddingBackground>();
private int hash = -1;
/**
* The "background-attachment" property.
*/
public final int backgroundAttachment;
/**
* The "background-color" property.
*/
public final Color backgroundColor;
/**
* The "background-image" property.
*/
public final String backgroundImage;
/**
* The "background-repeat" property.
*/
public final int backgroundRepeat;
/**
* The "background-position-horizontal" property.
*/
public final Length backgroundPositionHorizontal;
/**
* The "background-position-vertical" property.
*/
public final Length backgroundPositionVertical;
public final Length backgroungImageTargetWidth;
public final Length backgroungImageTargetHeight;
private ImageInfo backgroundImageInfo;
/** the "before" edge */
public static final int BEFORE = 0;
/** the "after" edge */
public static final int AFTER = 1;
/** the "start" edge */
public static final int START = 2;
/** the "end" edge */
public static final int END = 3;
/**
* Utility class to express border info.
*/
public static final class BorderInfo {
/** cache holding all canonical instances */
private static final PropertyCache<BorderInfo> CACHE
= new PropertyCache<BorderInfo>();
private int mStyle; // Enum for border style
private Color mColor; // Border color
private CondLengthProperty mWidth;
private CondLengthProperty radiusStart;
private CondLengthProperty radiusEnd;
private int hash = -1;
/**
* Hidden constructor
*/
private BorderInfo(int style, CondLengthProperty width, Color color,
CondLengthProperty radiusStart, CondLengthProperty radiusEnd) {
mStyle = style;
mWidth = width;
mColor = color;
this.radiusStart = radiusStart;
this.radiusEnd = radiusEnd;
}
/**
* Returns a BorderInfo instance corresponding to the given values.
*
* @param style the border-style
* @param width the border-width
* @param color the border-color
* @param radiusStart the start radius for rounded borders
* @param radiusEnd the end radius for rounded borders
* @return a cached BorderInfo instance
*/
public static BorderInfo getInstance(int style, CondLengthProperty width, Color color,
CondLengthProperty radiusStart, CondLengthProperty radiusEnd) {
return CACHE.fetch(new BorderInfo(style, width, color, radiusStart, radiusEnd));
}
/**
* @return the border-style
*/
public int getStyle() {
return this.mStyle;
}
/**
* @return the border-color
*/
public Color getColor() {
return this.mColor;
}
/**
* @return the border-width
*/
public CondLengthProperty getWidth() {
return this.mWidth;
}
/**
* Convenience method returning the border-width,
* taking into account values of "none" and "hidden"
*
* @return the retained border-width
*/
public int getRetainedWidth() {
if ((mStyle == Constants.EN_NONE)
|| (mStyle == Constants.EN_HIDDEN)) {
return 0;
} else {
return mWidth.getLengthValue();
}
}
/**
* @return the border-*-start-radius
*/
public CondLengthProperty getRadiusStart() {
return this.radiusStart;
}
/**
* @return the border-*-end-radius
*/
public CondLengthProperty getRadiusEnd() {
return this.radiusEnd;
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer("BorderInfo");
sb.append(" {");
sb.append(mStyle);
sb.append(", ");
sb.append(mColor);
sb.append(", ");
sb.append(mWidth);
sb.append(", ");
sb.append(radiusStart);
sb.append(", ");
sb.append(radiusEnd);
sb.append("}");
return sb.toString();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof BorderInfo)) {
return false;
}
BorderInfo bi = (BorderInfo)obj;
return (this.mColor == bi.mColor
&& this.mStyle == bi.mStyle
&& this.mWidth == bi.mWidth
&& this.radiusStart == bi.radiusStart
&& this.radiusEnd == bi.radiusEnd);
}
@Override
public int hashCode() {
if (this.hash == -1) {
int hash = 17;
hash = 37 * hash + (mColor == null ? 0 : mColor.hashCode());
hash = 37 * hash + mStyle;
hash = 37 * hash + (mWidth == null ? 0 : mWidth.hashCode());
hash = 37 * hash + (radiusStart == null ? 0 : radiusStart.hashCode());
hash = 37 * hash + (radiusEnd == null ? 0 : radiusEnd.hashCode());
this.hash = hash;
}
return this.hash;
}
}
/**
* A border info with style "none". Used as a singleton, in the collapsing-border model,
* for elements which don't specify any border on some of their sides.
*/
private static final BorderInfo DEFAULT_BORDER_INFO = BorderInfo.getInstance(
Constants.EN_NONE, new ConditionalNullLength(), null, new ConditionalNullLength(),
new ConditionalNullLength());
/**
* A conditional length of value 0. Returned by the
* {@link CommonBorderPaddingBackground#getBorderInfo(int)} method when the
* corresponding border isn't specified, to avoid to callers painful checks for null.
*/
private static class ConditionalNullLength extends CondLengthProperty {
@Override
public Property getComponent(int cmpId) {
throw new UnsupportedOperationException();
}
@Override
public Property getConditionality() {
throw new UnsupportedOperationException();
}
@Override
public Length getLength() {
throw new UnsupportedOperationException();
}
@Override
public Property getLengthComponent() {
throw new UnsupportedOperationException();
}
@Override
public int getLengthValue() {
return 0;
}
@Override
public int getLengthValue(PercentBaseContext context) {
return 0;
}
@Override
public boolean isDiscard() {
return true;
}
@Override
public void setComponent(int cmpId, Property cmpnValue, boolean isDefault) {
throw new UnsupportedOperationException();
}
@Override
public String toString() {
return "CondLength[0mpt, discard]";
}
}
/**
* Returns a default BorderInfo of style none.
*
* @return a BorderInfo instance with style set to {@link Constants#EN_NONE}
*/
public static BorderInfo getDefaultBorderInfo() {
return DEFAULT_BORDER_INFO;
}
private BorderInfo[] borderInfo = new BorderInfo[4];
private CondLengthProperty[] padding = new CondLengthProperty[4];
/**
* Construct a CommonBorderPaddingBackground object.
*
* @param pList The PropertyList to get properties from.
* @throws PropertyException if there's an error while binding the properties
*/
CommonBorderPaddingBackground(PropertyList pList) throws PropertyException {
backgroundAttachment = pList.get(Constants.PR_BACKGROUND_ATTACHMENT).getEnum();
Color bc = pList.get(Constants.PR_BACKGROUND_COLOR).getColor(
pList.getFObj().getUserAgent());
if (bc.getAlpha() == 0) {
backgroundColor = null;
} else {
backgroundColor = bc;
}
String img = pList.get(Constants.PR_BACKGROUND_IMAGE).getString();
if (img == null || "none".equals(img)) {
backgroundImage = "";
backgroundRepeat = -1;
backgroundPositionHorizontal = null;
backgroundPositionVertical = null;
} else {
backgroundImage = img;
backgroundRepeat = pList.get(Constants.PR_BACKGROUND_REPEAT).getEnum();
backgroundPositionHorizontal = pList.get(
Constants.PR_BACKGROUND_POSITION_HORIZONTAL).getLength();
backgroundPositionVertical = pList.get(
Constants.PR_BACKGROUND_POSITION_VERTICAL).getLength();
}
backgroungImageTargetWidth = pList.get(Constants.PR_X_BACKGROUND_IMAGE_WIDTH).getLength();
backgroungImageTargetHeight = pList.get(Constants.PR_X_BACKGROUND_IMAGE_HEIGHT).getLength();
initBorderInfo(pList, BEFORE,
Constants.PR_BORDER_BEFORE_COLOR,
Constants.PR_BORDER_BEFORE_STYLE,
Constants.PR_BORDER_BEFORE_WIDTH,
Constants.PR_PADDING_BEFORE,
Constants.PR_X_BORDER_BEFORE_RADIUS_START,
Constants.PR_X_BORDER_BEFORE_RADIUS_END);
initBorderInfo(pList, AFTER,
Constants.PR_BORDER_AFTER_COLOR,
Constants.PR_BORDER_AFTER_STYLE,
Constants.PR_BORDER_AFTER_WIDTH,
Constants.PR_PADDING_AFTER,
Constants.PR_X_BORDER_AFTER_RADIUS_START,
Constants.PR_X_BORDER_AFTER_RADIUS_END);
initBorderInfo(pList, START,
Constants.PR_BORDER_START_COLOR,
Constants.PR_BORDER_START_STYLE,
Constants.PR_BORDER_START_WIDTH,
Constants.PR_PADDING_START,
Constants.PR_X_BORDER_START_RADIUS_BEFORE,
Constants.PR_X_BORDER_START_RADIUS_AFTER);
initBorderInfo(pList, END,
Constants.PR_BORDER_END_COLOR,
Constants.PR_BORDER_END_STYLE,
Constants.PR_BORDER_END_WIDTH,
Constants.PR_PADDING_END,
Constants.PR_X_BORDER_END_RADIUS_BEFORE,
Constants.PR_X_BORDER_END_RADIUS_AFTER);
}
/**
* Obtain a CommonBorderPaddingBackground instance based on the
* related property valus in the given {@link PropertyList}
*
* @param pList the {@link PropertyList} to use
* @return a CommonBorderPaddingBackground instance (cached if possible)
* @throws PropertyException in case of an error
*/
public static CommonBorderPaddingBackground getInstance(PropertyList pList) throws PropertyException {
CommonBorderPaddingBackground newInstance = new CommonBorderPaddingBackground(pList);
CommonBorderPaddingBackground cachedInstance = null;
/* if padding-* and background-position-* resolve to absolute lengths
* the whole instance can be cached */
if ((newInstance.padding[BEFORE] == null || newInstance.padding[BEFORE].getLength().isAbsolute())
&& (newInstance.padding[AFTER] == null || newInstance.padding[AFTER].getLength().isAbsolute())
&& (newInstance.padding[START] == null || newInstance.padding[START].getLength().isAbsolute())
&& (newInstance.padding[END] == null || newInstance.padding[END].getLength().isAbsolute())
&& (newInstance.backgroundPositionHorizontal == null || newInstance.backgroundPositionHorizontal
.isAbsolute())
&& (newInstance.backgroundPositionVertical == null || newInstance.backgroundPositionVertical
.isAbsolute())
&& (newInstance.backgroungImageTargetHeight == null || newInstance.backgroungImageTargetHeight
.isAbsolute())
&& (newInstance.backgroungImageTargetWidth == null || newInstance.backgroungImageTargetWidth
.isAbsolute())) {
cachedInstance = CACHE.fetch(newInstance);
}
synchronized (newInstance.backgroundImage.intern()) {
/* for non-cached, or not-yet-cached instances, preload the image */
if ((cachedInstance == null || cachedInstance == newInstance)
&& !("".equals(newInstance.backgroundImage))) {
//Additional processing: preload image
String uri = URISpecification.getURL(newInstance.backgroundImage);
FObj fobj = pList.getFObj();
FOUserAgent userAgent = pList.getFObj().getUserAgent();
ImageManager manager = userAgent.getImageManager();
ImageSessionContext sessionContext = userAgent.getImageSessionContext();
ImageInfo info;
try {
info = manager.getImageInfo(uri, sessionContext);
newInstance.backgroundImageInfo = info;
} catch (ImageException e) {
ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
fobj.getUserAgent().getEventBroadcaster());
eventProducer.imageError(fobj, uri, e, fobj.getLocator());
} catch (FileNotFoundException fnfe) {
ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
fobj.getUserAgent().getEventBroadcaster());
eventProducer.imageNotFound(fobj, uri, fnfe, fobj.getLocator());
} catch (IOException ioe) {
ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
fobj.getUserAgent().getEventBroadcaster());
eventProducer.imageIOError(fobj, uri, ioe, fobj.getLocator());
}
}
}
return (cachedInstance != null ? cachedInstance : newInstance);
}
private void initBorderInfo(PropertyList pList, int side,
int colorProp, int styleProp, int widthProp, int paddingProp,
int radiusStartProp, int radiusEndProp)
throws PropertyException {
padding[side] = pList.get(paddingProp).getCondLength();
// If style = none, force width to 0, don't get Color (spec 7.7.20)
int style = pList.get(styleProp).getEnum();
FOUserAgent ua = pList.getFObj().getUserAgent();
setBorderInfo(BorderInfo.getInstance(style,
pList.get(widthProp).getCondLength(),
pList.get(colorProp).getColor(ua),
pList.get(radiusStartProp).getCondLength(),
pList.get(radiusEndProp).getCondLength()), side);
}
/**
* Sets a border.
* @param info the border information
* @param side the side to apply the info to
*/
private void setBorderInfo(BorderInfo info, int side) {
this.borderInfo[side] = info;
}
/**
* @param side the side to retrieve
* @return the border info for a side
*/
public BorderInfo getBorderInfo(int side) {
if (this.borderInfo[side] == null) {
return getDefaultBorderInfo();
} else {
return this.borderInfo[side];
}
}
/**
* @return the background image info object, null if there is
* no background image.
*/
public ImageInfo getImageInfo() {
return this.backgroundImageInfo;
}
/**
* @param discard indicates whether the .conditionality component should be
* considered (start of a reference-area)
* @return the width of the start-border, taking into account the specified conditionality
*/
public int getBorderStartWidth(boolean discard) {
return getBorderWidth(START, discard);
}
/**
* @param discard indicates whether the .conditionality component should be
* considered (end of a reference-area)
* @return the width of the end-border, taking into account the specified conditionality
*/
public int getBorderEndWidth(boolean discard) {
return getBorderWidth(END, discard);
}
/**
* @param discard indicates whether the .conditionality component should be
* considered (start of a reference-area)
* @return the width of the before-border, taking into account the specified conditionality
*/
public int getBorderBeforeWidth(boolean discard) {
return getBorderWidth(BEFORE, discard);
}
/**
* @param discard indicates whether the .conditionality component should be
* considered (end of a reference-area)
* @return the width of the after-border, taking into account the specified conditionality
*/
public int getBorderAfterWidth(boolean discard) {
return getBorderWidth(AFTER, discard);
}
/**
* @param discard indicates whether the .conditionality component should be
* considered (start of a reference-area)
* @param context the context to evaluate percentage values
* @return the width of the start-padding, taking into account the specified conditionality
*/
public int getPaddingStart(boolean discard, PercentBaseContext context) {
return getPadding(START, discard, context);
}
/**
* @param discard indicates whether the .conditionality component should be
* considered (start of a reference-area)
* @param context the context to evaluate percentage values
* @return the width of the end-padding, taking into account the specified conditionality
*/
public int getPaddingEnd(boolean discard, PercentBaseContext context) {
return getPadding(END, discard, context);
}
/**
* @param discard indicates whether the .conditionality component should be
* considered (start of a reference-area)
* @param context the context to evaluate percentage values
* @return the width of the before-padding, taking into account the specified conditionality
*/
public int getPaddingBefore(boolean discard, PercentBaseContext context) {
return getPadding(BEFORE, discard, context);
}
/**
* @param discard indicates whether the .conditionality component should be
* considered (start of a reference-area)
* @param context the context to evaluate percentage values
* @return the width of the after-padding, taking into account the specified conditionality
*/
public int getPaddingAfter(boolean discard, PercentBaseContext context) {
return getPadding(AFTER, discard, context);
}
/**
* @param side the side of the border
* @param discard indicates whether the .conditionality component should be considered (end of a
* reference-area)
* @return the width of the start-border, taking into account the specified conditionality
*/
public int getBorderWidth(int side, boolean discard) {
if ((borderInfo[side] == null)
|| (borderInfo[side].mStyle == Constants.EN_NONE)
|| (borderInfo[side].mStyle == Constants.EN_HIDDEN)
|| (discard && borderInfo[side].mWidth.isDiscard())) {
return 0;
} else {
return borderInfo[side].mWidth.getLengthValue();
}
}
/**
* Returns the border corner radius of the starting edge
* i.e. the edge either adjacent to the before or start border.
* @param side the border side
* @param discard indicates whether the .conditionality component should be
* considered (end of a reference-area)
* @param context the context for percentage calculations
* @return the border radius of the of the starting corner
*/
public int getBorderRadiusStart(int side, boolean discard, PercentBaseContext context) {
if (borderInfo[side] == null) {
return 0;
} else {
return borderInfo[side].radiusStart.getLengthValue(context);
}
}
/**
* Returns the border corner radius of the ending edge
* i.e. the edge either adjacent to the after or end border
* @param side the border side
* @param discard indicates whether the .conditionality component should be
* considered (end of a reference-area)
* @param context the context for percentage calculations
* @return the border radius of the of the ending corner
*/
public int getBorderRadiusEnd(int side, boolean discard, PercentBaseContext context) {
if (borderInfo[side] == null) {
return 0;
} else {
return borderInfo[side].radiusEnd.getLengthValue(context);
}
}
/**
* The border-color for the given side
*
* @param side one of {@link #BEFORE}, {@link #AFTER}, {@link #START}, {@link #END}
* @return the border-color for the given side
*/
public Color getBorderColor(int side) {
if (borderInfo[side] != null) {
return borderInfo[side].getColor();
} else {
return null;
}
}
/**
* The border-style for the given side
*
* @param side one of {@link #BEFORE}, {@link #AFTER}, {@link #START}, {@link #END}
* @return the border-style for the given side
*/
public int getBorderStyle(int side) {
if (borderInfo[side] != null) {
return borderInfo[side].mStyle;
} else {
return Constants.EN_NONE;
}
}
/**
* Return the padding for the given side, taking into account
* the conditionality and evaluating any percentages in the given
* context.
*
* @param side one of {@link #BEFORE}, {@link #AFTER}, {@link #START}, {@link #END}
* @param discard true if the conditionality component should be considered
* @param context the context for percentage-resolution
* @return the computed padding for the given side
*/
public int getPadding(int side, boolean discard, PercentBaseContext context) {
if ((padding[side] == null) || (discard && padding[side].isDiscard())) {
return 0;
} else {
return padding[side].getLengthValue(context);
}
}
/**
* Returns the CondLengthProperty for the padding on one side.
* @param side the side
* @return the requested CondLengthProperty
*/
public CondLengthProperty getPaddingLengthProperty(int side) {
return padding[side];
}
/**
* Return all the border and padding width in the inline progression
* dimension.
* @param discard the discard flag.
* @param context for percentage evaluation.
* @return all the padding and border width.
*/
public int getIPPaddingAndBorder(boolean discard, PercentBaseContext context) {
return getPaddingStart(discard, context)
+ getPaddingEnd(discard, context)
+ getBorderStartWidth(discard)
+ getBorderEndWidth(discard);
}
/**
* Return all the border and padding height in the block progression
* dimension.
* @param discard the discard flag.
* @param context for percentage evaluation
* @return all the padding and border height.
*/
public int getBPPaddingAndBorder(boolean discard, PercentBaseContext context) {
return getPaddingBefore(discard, context) + getPaddingAfter(discard, context)
+ getBorderBeforeWidth(discard) + getBorderAfterWidth(discard);
}
@Override
public String toString() {
return "CommonBordersAndPadding (Before, After, Start, End):\n"
+ "Borders: (" + getBorderBeforeWidth(false) + ", " + getBorderAfterWidth(false) + ", "
+ getBorderStartWidth(false) + ", " + getBorderEndWidth(false) + ")\n"
+ "Border Colors: (" + getBorderColor(BEFORE) + ", " + getBorderColor(AFTER) + ", "
+ getBorderColor(START) + ", " + getBorderColor(END) + ")\n"
+ "Padding: (" + getPaddingBefore(false, null) + ", " + getPaddingAfter(false, null)
+ ", " + getPaddingStart(false, null) + ", " + getPaddingEnd(false, null) + ")\n";
}
/**
* @return true if there is any kind of background to be painted
*/
public boolean hasBackground() {
return ((backgroundColor != null || getImageInfo() != null));
}
/** @return true if border is non-zero. */
public boolean hasBorder() {
return ((getBorderBeforeWidth(false) + getBorderAfterWidth(false)
+ getBorderStartWidth(false) + getBorderEndWidth(false)) > 0);
}
/**
* @param context for percentage based evaluation.
* @return true if padding is non-zero.
*/
public boolean hasPadding(PercentBaseContext context) {
return ((getPaddingBefore(false, context) + getPaddingAfter(false, context)
+ getPaddingStart(false, context) + getPaddingEnd(false, context)) > 0);
}
/** @return true if there are any borders defined. */
public boolean hasBorderInfo() {
return (borderInfo[BEFORE] != null || borderInfo[AFTER] != null
|| borderInfo[START] != null || borderInfo[END] != null);
}
/**
* Returns the "background-color" property.
* @return the "background-color" property.
*/
public Color getBackgroundColor() {
return backgroundColor;
}
/**
* Returns the "background-attachment" property.
* @return the "background-attachment" property.
*/
public int getBackgroundAttachment() {
return backgroundAttachment;
}
/**
* Returns the "background-image" property.
* @return the "background-image" property.
*/
public String getBackgroundImage() {
return backgroundImage;
}
/**
* Returns the "background-repeat" property.
* @return the "background-repeat" property.
*/
public int getBackgroundRepeat() {
return backgroundRepeat;
}
/**
* Returns the "background-position-horizontal" property.
* @return the "background-position-horizontal" property.
*/
public Length getBackgroundPositionHorizontal() {
return backgroundPositionHorizontal;
}
/**
* Returns the "background-position-vertical" property.
* @return the "background-position-vertical" property.
*/
public Length getBackgroundPositionVertical() {
return backgroundPositionVertical;
}
/**
* Returns the background image info
* @return the background image info
*/
public ImageInfo getBackgroundImageInfo() {
return backgroundImageInfo;
}
/**
* Returns the border info
* @return the border info
*/
public BorderInfo[] getBorderInfo() {
return borderInfo;
}
/**
* Returns the padding
* @return the padding
*/
public CondLengthProperty[] getPadding() {
return padding;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof CommonBorderPaddingBackground) {
CommonBorderPaddingBackground cbpb = (CommonBorderPaddingBackground)obj;
return (this.backgroundAttachment == cbpb.backgroundAttachment
&& this.backgroundColor == cbpb.backgroundColor
&& this.backgroundImage.equals(cbpb.backgroundImage)
&& this.backgroundPositionHorizontal == cbpb.backgroundPositionHorizontal
&& this.backgroundPositionVertical == cbpb.backgroundPositionVertical
&& this.backgroundRepeat == cbpb.backgroundRepeat
&& Arrays.equals(borderInfo, cbpb.borderInfo)
&& Arrays.equals(padding, cbpb.padding));
} else {
return false;
}
}
@Override
public int hashCode() {
if (this.hash == -1) {
int hash = getHashCode(backgroundColor,
backgroundImage,
backgroundPositionHorizontal,
backgroundPositionVertical,
backgroungImageTargetWidth,
backgroungImageTargetHeight,
borderInfo[BEFORE],
borderInfo[AFTER],
borderInfo[START],
borderInfo[END],
padding[BEFORE],
padding[AFTER],
padding[START],
padding[END]);
hash = 37 * hash + backgroundAttachment;
hash = 37 * hash + backgroundRepeat;
this.hash = hash;
}
return this.hash;
}
private int getHashCode(Object... objects) {
int hash = 17;
for (Object o : objects) {
hash = 37 * hash + (o == null ? 0 : o.hashCode());
}
return hash;
}
}