1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 /* $Id: LengthRangeProperty.java 1805173 2017-08-16 10:50:04Z ssteiner $ */ 19 20 package org.apache.fop.fo.properties; 21 22 import org.apache.fop.datatypes.CompoundDatatype; 23 import org.apache.fop.datatypes.Length; 24 import org.apache.fop.datatypes.PercentBaseContext; 25 import org.apache.fop.fo.FObj; 26 import org.apache.fop.fo.PropertyList; 27 import org.apache.fop.fo.expr.PropertyException; 28 import org.apache.fop.traits.MinOptMax; 29 import org.apache.fop.util.CompareUtil; 30 31 /** 32 * Superclass for properties that contain LengthRange values 33 */ 34 public class LengthRangeProperty extends Property implements CompoundDatatype { 35 private Property minimum; 36 private Property optimum; 37 private Property maximum; 38 private static final int MINSET = 1; 39 private static final int OPTSET = 2; 40 private static final int MAXSET = 4; 41 private int bfSet; // bit field 42 private boolean consistent; 43 44 /** 45 * Converts this <code>LengthRangeProperty</code> to a <code>MinOptMax</code>. 46 * 47 * @param context Percentage evaluation context 48 * @return the requested MinOptMax instance 49 */ toMinOptMax(PercentBaseContext context)50 public MinOptMax toMinOptMax(PercentBaseContext context) { 51 int min = getMinimum(context).isAuto() ? 0 52 : getMinimum(context).getLength().getValue(context); 53 int opt = getOptimum(context).isAuto() ? min 54 : getOptimum(context).getLength().getValue(context); 55 int max = getMaximum(context).isAuto() ? Integer.MAX_VALUE 56 : getMaximum(context).getLength().getValue(context); 57 return MinOptMax.getInstance(min, opt, max); 58 } 59 60 /** 61 * Inner class for a Maker for LengthProperty objects 62 */ 63 public static class Maker extends CompoundPropertyMaker { 64 65 /** 66 * @param propId the id of the property for which a Maker should be created 67 */ Maker(int propId)68 public Maker(int propId) { 69 super(propId); 70 } 71 72 /** 73 * Create a new empty instance of LengthRangeProperty. 74 * @return the new instance. 75 */ makeNewProperty()76 public Property makeNewProperty() { 77 return new LengthRangeProperty(); 78 } 79 isNegativeLength(Length len)80 private boolean isNegativeLength(Length len) { 81 return ((len instanceof PercentLength 82 && ((PercentLength) len).getPercentage() < 0) 83 || (len.isAbsolute() && len.getValue() < 0)); 84 } 85 86 /** {@inheritDoc} */ convertProperty(Property p, PropertyList propertyList, FObj fo)87 public Property convertProperty(Property p, 88 PropertyList propertyList, FObj fo) 89 throws PropertyException { 90 91 if (p instanceof LengthRangeProperty) { 92 return p; 93 } 94 95 if (this.propId == PR_BLOCK_PROGRESSION_DIMENSION 96 || this.propId == PR_INLINE_PROGRESSION_DIMENSION) { 97 Length len = p.getLength(); 98 if (len != null) { 99 if (isNegativeLength(len)) { 100 log.warn(FObj.decorateWithContextInfo( 101 "Replaced negative value (" + len + ") for " + getName() 102 + " with 0mpt", fo)); 103 p = FixedLength.ZERO_FIXED_LENGTH; 104 } 105 } 106 } 107 108 return super.convertProperty(p, propertyList, fo); 109 } 110 111 112 /** 113 * {@inheritDoc} 114 */ setSubprop(Property baseProperty, int subpropertyId, Property subproperty)115 protected Property setSubprop(Property baseProperty, int subpropertyId, 116 Property subproperty) { 117 CompoundDatatype val = (CompoundDatatype) baseProperty.getObject(); 118 if (this.propId == PR_BLOCK_PROGRESSION_DIMENSION 119 || this.propId == PR_INLINE_PROGRESSION_DIMENSION) { 120 Length len = subproperty.getLength(); 121 if (len != null) { 122 if (isNegativeLength(len)) { 123 log.warn("Replaced negative value (" + len + ") for " + getName() 124 + " with 0mpt"); 125 val.setComponent(subpropertyId, 126 FixedLength.ZERO_FIXED_LENGTH, false); 127 return baseProperty; 128 } 129 } 130 } 131 val.setComponent(subpropertyId, subproperty, false); 132 return baseProperty; 133 } 134 135 } 136 137 138 139 /** 140 * {@inheritDoc} 141 */ setComponent(int cmpId, Property cmpnValue, boolean bIsDefault)142 public void setComponent(int cmpId, Property cmpnValue, 143 boolean bIsDefault) { 144 if (cmpId == CP_MINIMUM) { 145 setMinimum(cmpnValue, bIsDefault); 146 } else if (cmpId == CP_OPTIMUM) { 147 setOptimum(cmpnValue, bIsDefault); 148 } else if (cmpId == CP_MAXIMUM) { 149 setMaximum(cmpnValue, bIsDefault); 150 } 151 } 152 153 /** 154 * {@inheritDoc} 155 */ getComponent(int cmpId)156 public Property getComponent(int cmpId) { 157 if (cmpId == CP_MINIMUM) { 158 return getMinimum(null); 159 } else if (cmpId == CP_OPTIMUM) { 160 return getOptimum(null); 161 } else if (cmpId == CP_MAXIMUM) { 162 return getMaximum(null); 163 } else { 164 return null; // SHOULDN'T HAPPEN 165 } 166 } 167 168 /** 169 * Set minimum value to min. 170 * @param minimum A Length value specifying the minimum value for this 171 * LengthRange. 172 * @param bIsDefault If true, this is set as a "default" value 173 * and not a user-specified explicit value. 174 */ setMinimum(Property minimum, boolean bIsDefault)175 protected void setMinimum(Property minimum, boolean bIsDefault) { 176 this.minimum = minimum; 177 if (!bIsDefault) { 178 bfSet |= MINSET; 179 } 180 consistent = false; 181 } 182 183 184 /** 185 * Set maximum value to max if it is >= optimum or optimum isn't set. 186 * @param max A Length value specifying the maximum value for this 187 * @param bIsDefault If true, this is set as a "default" value 188 * and not a user-specified explicit value. 189 */ setMaximum(Property max, boolean bIsDefault)190 protected void setMaximum(Property max, boolean bIsDefault) { 191 maximum = max; 192 if (!bIsDefault) { 193 bfSet |= MAXSET; 194 } 195 consistent = false; 196 } 197 198 199 /** 200 * Set the optimum value. 201 * @param opt A Length value specifying the optimum value for this 202 * @param bIsDefault If true, this is set as a "default" value 203 * and not a user-specified explicit value. 204 */ setOptimum(Property opt, boolean bIsDefault)205 protected void setOptimum(Property opt, boolean bIsDefault) { 206 optimum = opt; 207 if (!bIsDefault) { 208 bfSet |= OPTSET; 209 } 210 consistent = false; 211 } 212 213 // Minimum is prioritaire, if explicit checkConsistency(PercentBaseContext context)214 private void checkConsistency(PercentBaseContext context) { 215 if (consistent) { 216 return; 217 } 218 if (context == null) { 219 return; 220 } 221 // Make sure max >= min 222 // Must also control if have any allowed enum values! 223 224 if (!minimum.isAuto() && !maximum.isAuto() 225 && minimum.getLength().getValue(context) > maximum.getLength().getValue(context)) { 226 if ((bfSet & MINSET) != 0) { 227 // if minimum is explicit, force max to min 228 if ((bfSet & MAXSET) != 0) { 229 // Warning: min>max, resetting max to min 230 log.error("forcing max to min in LengthRange"); 231 } 232 maximum = minimum; 233 } else { 234 minimum = maximum; // minimum was default value 235 } 236 } 237 // Now make sure opt <= max and opt >= min 238 if (!optimum.isAuto() && !maximum.isAuto() 239 && optimum.getLength().getValue(context) > maximum.getLength().getValue(context)) { 240 if ((bfSet & OPTSET) != 0) { 241 if ((bfSet & MAXSET) != 0) { 242 // Warning: opt > max, resetting opt to max 243 log.error("forcing opt to max in LengthRange"); 244 optimum = maximum; 245 } else { 246 maximum = optimum; // maximum was default value 247 } 248 } else { 249 // opt is default and max is explicit or default 250 optimum = maximum; 251 } 252 } else if (!optimum.isAuto() && !minimum.isAuto() 253 && optimum.getLength().getValue(context) 254 < minimum.getLength().getValue(context)) { 255 if ((bfSet & MINSET) != 0) { 256 // if minimum is explicit, force opt to min 257 if ((bfSet & OPTSET) != 0) { 258 log.error("forcing opt to min in LengthRange"); 259 } 260 optimum = minimum; 261 } else { 262 minimum = optimum; // minimum was default value 263 } 264 } 265 266 consistent = true; 267 } 268 269 /** 270 * @param context Percentage evaluation context 271 * @return minimum length 272 */ getMinimum(PercentBaseContext context)273 public Property getMinimum(PercentBaseContext context) { 274 checkConsistency(context); 275 return this.minimum; 276 } 277 278 /** 279 * @param context Percentage evaluation context 280 * @return maximum length 281 */ getMaximum(PercentBaseContext context)282 public Property getMaximum(PercentBaseContext context) { 283 checkConsistency(context); 284 return this.maximum; 285 } 286 287 /** 288 * @param context Percentage evaluation context 289 * @return optimum length 290 */ getOptimum(PercentBaseContext context)291 public Property getOptimum(PercentBaseContext context) { 292 checkConsistency(context); 293 return this.optimum; 294 } 295 296 /** {@inheritDoc} */ toString()297 public String toString() { 298 return "LengthRange[" 299 + "min:" + getMinimum(null).getObject() 300 + ", max:" + getMaximum(null).getObject() 301 + ", opt:" + getOptimum(null).getObject() + "]"; 302 } 303 304 /** 305 * @return this.lengthRange 306 */ getLengthRange()307 public LengthRangeProperty getLengthRange() { 308 return this; 309 } 310 311 /** 312 * @return this.lengthRange cast as an Object 313 */ getObject()314 public Object getObject() { 315 return this; 316 } 317 318 @Override hashCode()319 public int hashCode() { 320 final int prime = 31; 321 int result = 1; 322 result = prime * result + bfSet; 323 result = prime * result + (consistent ? 1231 : 1237); 324 result = prime * result + CompareUtil.getHashCode(minimum); 325 result = prime * result + CompareUtil.getHashCode(optimum); 326 result = prime * result + CompareUtil.getHashCode(maximum); 327 return result; 328 } 329 330 @Override equals(Object obj)331 public boolean equals(Object obj) { 332 if (this == obj) { 333 return true; 334 } 335 if (!(obj instanceof LengthRangeProperty)) { 336 return false; 337 } 338 LengthRangeProperty other = (LengthRangeProperty) obj; 339 return bfSet == other.bfSet 340 && consistent == other.consistent 341 && CompareUtil.equal(minimum, other.minimum) 342 && CompareUtil.equal(optimum, other.optimum) 343 && CompareUtil.equal(maximum, other.maximum); 344 } 345 } 346