1 /*
   2  * Copyright (c) 2008, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.javafx.css;
  27 
  28 import com.sun.javafx.css.converters.URLConverter;
  29 import javafx.css.ParsedValue;
  30 import javafx.css.StyleConverter;
  31 import javafx.css.StyleOrigin;
  32 
  33 import java.io.DataInputStream;
  34 import java.io.DataOutputStream;
  35 import java.io.IOException;
  36 
  37 final public class Declaration {
  38     final String property;
  39     final ParsedValueImpl parsedValue;
  40     final boolean important;   
  41     // The Rule to which this Declaration belongs.
  42     Rule rule;
  43 
  44     public Declaration(final String propertyName, final ParsedValueImpl parsedValue,
  45             final boolean important) {
  46         this.property = propertyName;
  47         this.parsedValue = parsedValue;
  48         this.important = important;
  49         if (propertyName == null) { 
  50             throw new IllegalArgumentException("propertyName cannot be null");
  51         }
  52         if (parsedValue == null) { 
  53             throw new IllegalArgumentException("parsedValue cannot be null");
  54         }
  55     }
  56 
  57     /** @return ParsedValue contains the parsed declaration. */
  58     public ParsedValue getParsedValue() {
  59         return parsedValue;
  60     }
  61     
  62     /** @return ParsedValue contains the parsed declaration. */
  63     ParsedValueImpl getParsedValueImpl() {
  64         return parsedValue;
  65     }
  66     
  67     /** @return The CSS property name */
  68     public String getProperty() {
  69         return property;
  70     }
  71     
  72     /** @return The Rule to which this Declaration belongs. */
  73     public Rule getRule() {
  74         return rule;
  75     }
  76     
  77     public boolean isImportant() {
  78         return important;
  79     }
  80 
  81     /** Helper */
  82     private StyleOrigin getOrigin() {
  83         Rule rule = getRule();
  84         if (rule != null)  {
  85             return rule.getOrigin();
  86         }
  87         return null;
  88     }
  89     /** 
  90      * One declaration is the equal to another regardless of the Rule to which
  91      * the Declaration belongs. Only the property, value and importance are
  92      * considered.
  93      */
  94     @Override
  95     public boolean equals(Object obj) {
  96         if (this == obj) {
  97             return true;
  98         }
  99         if (obj == null) {
 100             return false;
 101         }
 102         if (getClass() != obj.getClass()) {
 103             return false;
 104         }
 105         final Declaration other = (Declaration) obj;
 106         if (this.important != other.important) {
 107             return false;
 108         }
 109         if (this.getOrigin() != other.getOrigin()) {
 110             return false;
 111         }
 112         if ((this.property == null) ? (other.property != null) : !this.property.equals(other.property)) {
 113             return false;
 114         }
 115         if (this.parsedValue != other.parsedValue && (this.parsedValue == null || !this.parsedValue.equals(other.parsedValue))) {
 116             return false;
 117         }
 118         return true;
 119     }
 120 
 121     @Override
 122     public int hashCode() {
 123         int hash = 5;
 124         hash = 89 * hash + (this.property != null ? this.property.hashCode() : 0);
 125         hash = 89 * hash + (this.parsedValue != null ? this.parsedValue.hashCode() : 0);
 126         hash = 89 * hash + (this.important ? 1 : 0);
 127         return hash;
 128     }
 129 
 130     @Override public String toString() {
 131         StringBuilder sbuf = new StringBuilder(property);
 132         sbuf.append(": ");
 133         sbuf.append(parsedValue);
 134         if (important) sbuf.append(" !important");
 135         return sbuf.toString();
 136     }
 137     
 138     //
 139     // RT-21964
 140     //
 141     // We know when the .css file is parsed what the stylesheet URL is,
 142     // but that might not be the URL of the deployed file. So for URL
 143     // types, the parser inserts a null placeholder for the URL and we
 144     // fix it up here. This method is called from Rule#setStylesheet
 145     // and from Rule#declarations onChanged method.
 146     // 
 147     void fixUrl(String stylesheetUrl) {
 148         
 149         if (stylesheetUrl == null) return;
 150         
 151         final StyleConverter converter = parsedValue.getConverter();        
 152         
 153         // code is tightly coupled to the way URLConverter works
 154         if (converter == URLConverter.getInstance()) {
 155             
 156             final ParsedValue[] values = (ParsedValue[])parsedValue.getValue();
 157             values[1] = new ParsedValueImpl<String,String>(stylesheetUrl, null);
 158             
 159         } else if (converter == URLConverter.SequenceConverter.getInstance()) {
 160 
 161             final ParsedValue<ParsedValue[], String>[] layers = 
 162                 (ParsedValue<ParsedValue[], String>[])parsedValue.getValue();
 163             
 164             for (int layer = 0; layer < layers.length; layer++) {
 165                 final ParsedValue[] values = layers[layer].getValue();
 166                 values[1] = new ParsedValueImpl<String,String>(stylesheetUrl, null);
 167             }
 168             
 169         }
 170                 
 171     }
 172 
 173     final void writeBinary(final DataOutputStream os, final StringStore stringStore)
 174         throws IOException
 175     {
 176         os.writeShort(stringStore.addString(getProperty()));
 177         getParsedValueImpl().writeBinary(os,stringStore);
 178         os.writeBoolean(isImportant());            
 179     }
 180 
 181     static Declaration readBinary(int bssVersion, DataInputStream is, String[] strings)
 182         throws IOException
 183     {
 184         final String propertyName = strings[is.readShort()];
 185         final ParsedValueImpl parsedValue = ParsedValueImpl.readBinary(bssVersion,is,strings);
 186         final boolean important = is.readBoolean();
 187         return new Declaration(propertyName, parsedValue, important);
 188             
 189     }
 190 }
 191