001package com.hfg.css;
002
003import java.awt.Color;
004import java.util.List;
005import java.util.Map;
006import java.util.HashMap;
007import java.util.regex.Pattern;
008import java.util.regex.Matcher;
009
010import com.hfg.graphics.ColorUtil;
011import com.hfg.util.StringBuilderPlus;
012import com.hfg.util.StringUtil;
013import com.hfg.xml.XMLTag;
014
015//------------------------------------------------------------------------------
016/**
017 * Contains CSS-related utility functions.
018 *
019 * @author J. Alex Taylor, hairyfatguy.com
020 */
021//------------------------------------------------------------------------------
022// com.hfg XML/HTML Coding Library
023//
024// This library is free software; you can redistribute it and/or
025// modify it under the terms of the GNU Lesser General Public
026// License as published by the Free Software Foundation; either
027// version 2.1 of the License, or (at your option) any later version.
028//
029// This library is distributed in the hope that it will be useful,
030// but WITHOUT ANY WARRANTY; without even the implied warranty of
031// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
032// Lesser General Public License for more details.
033//
034// You should have received a copy of the GNU Lesser General Public
035// License along with this library; if not, write to the Free Software
036// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
037//
038// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com
039// jataylor@hairyfatguy.com
040//------------------------------------------------------------------------------
041
042public class CssUtil
043{
044   //###########################################################################
045   // PUBLIC FUNCTIONS
046   //###########################################################################
047
048   //--------------------------------------------------------------------------
049   public static String colorToCssValue(Color inValue)
050   {
051      String value = "";
052      if (inValue != null)
053      {
054         if (255 == inValue.getAlpha())
055         {
056            // No opacity
057            value = "#" + ColorUtil.colorToHex(inValue);
058         }
059         else
060         {
061            // Use rgba() format to specify the opacity
062            value = "rgba(" + inValue.getRed() + ", " + inValue.getGreen() + ", " + inValue.getBlue()
063                    + ", " + String.format("%.1f", inValue.getAlpha()/255f)+ ")";
064         }
065      }
066
067      return value;
068   }
069
070   //--------------------------------------------------------------------------
071   /**
072    Adds the specified style attribute content to the specified tag.
073    @param inTag    extends XMLTag instead of HTMLTag so that the SVG classes can use it as well.
074    @param inValue  style value to add
075    */
076   public static void addStyle(XMLTag inTag, String inValue)
077   {
078      if (StringUtil.isSet(inValue))
079      {
080         String oldValue = inTag.getAttributeValue(CSS.STYLE);
081         if (StringUtil.isSet(oldValue))
082         {
083            String[] attrPairs = inValue.trim().split(";");
084            Map<String, String> styleAttrMap = new HashMap<>(attrPairs.length);
085            for (String pair : attrPairs)
086            {
087               String[] pieces = pair.split(":");
088               styleAttrMap.put(pieces[0].trim(), pieces[1].trim());
089            }
090
091            StringBuilderPlus buffer = new StringBuilderPlus(oldValue.trim()).setDelimiter(";");
092            for (String attrName : styleAttrMap.keySet())
093            {
094               boolean newAttr = true;
095               if (buffer.indexOf(attrName) >= 0)
096               {
097                  // The specified attribute may already be present. Check more carefully
098                  Pattern pattern = Pattern.compile("(?:^|;|\\s)" + attrName + "\\s*:\\s*(.+?)(?:;|$)");
099                  Matcher m = pattern.matcher(buffer.toString());
100                  if (m.find())
101                  {
102                     if (styleAttrMap.get(attrName).contains("!important")
103                           || ! m.group(1).contains("!important"))
104                     {
105                        buffer.replace(m.start(1), m.end(1), styleAttrMap.get(attrName));
106                     }
107
108                     newAttr = false;
109                  }
110               }
111
112               if (newAttr)
113               {
114                  // Add the new style attribute
115                  if (buffer.charAt(buffer.length() -1) == ';')
116                  {
117                     buffer.append(' ');
118                     buffer.append(attrName);
119                  }
120                  else
121                  {
122                     buffer.delimitedAppend(attrName);
123                  }
124
125                  buffer.append(":");
126                  buffer.append(styleAttrMap.get(attrName));
127               }
128
129            }
130
131            inValue = buffer.toString();
132         }
133
134         inTag.setAttribute(CSS.STYLE, inValue);
135      }
136   }
137
138   //--------------------------------------------------------------------------
139   /**
140    Removes the specified CSS property from the style attribute content of the specified tag if it is present.
141    @param inTag    extends XMLTag instead of HTMLTag so that the SVG classes can use it as well.
142    @param inProperty CSS property to remove
143    */
144   public static void removeStyleProperty(XMLTag inTag, CSSProperty inProperty)
145   {
146      if (inProperty != null)
147      {
148         String oldValue = inTag.getAttributeValue(CSS.STYLE);
149         if (StringUtil.isSet(oldValue))
150         {
151            List<CSSDeclaration> declarations = CSSDeclaration.parse(oldValue);
152            boolean containsProperty = false;
153            for (int i = 0; i < declarations.size(); i++)
154            {
155               CSSDeclaration declaration = declarations.get(i);
156               if (declaration.getProperty().equals(inProperty))
157               {
158                  containsProperty = true;
159                  declarations.remove(i);
160                  break;
161               }
162            }
163
164            // It the property wasn't present there is nothing to do
165            if (containsProperty)
166            {
167               inTag.setAttribute(CSS.STYLE, StringUtil.join(declarations, ";"));
168            }
169         }
170      }
171   }
172}