001    /*
002     * StyleAttribute.java
003     *
004     *
005     *  The Salamander Project - 2D and 3D graphics libraries in Java
006     *  Copyright (C) 2004 Mark McKay
007     *
008     *  This library is free software; you can redistribute it and/or
009     *  modify it under the terms of the GNU Lesser General Public
010     *  License as published by the Free Software Foundation; either
011     *  version 2.1 of the License, or (at your option) any later version.
012     *
013     *  This library is distributed in the hope that it will be useful,
014     *  but WITHOUT ANY WARRANTY; without even the implied warranty of
015     *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
016     *  Lesser General Public License for more details.
017     *
018     *  You should have received a copy of the GNU Lesser General Public
019     *  License along with this library; if not, write to the Free Software
020     *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
021     *
022     *  Mark McKay can be contacted at mark@kitfox.com.  Salamander and other
023     *  projects can be found at http://www.kitfox.com
024     *
025     * Created on January 27, 2004, 2:53 PM
026     */
027    
028    package com.kitfox.svg.xml;
029    
030    import java.awt.*;
031    import java.net.*;
032    import java.io.*;
033    import java.util.regex.*;
034    
035    /**
036     * @author Mark McKay
037     * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
038     */
039    public class StyleAttribute implements Serializable
040    {
041        public static final long serialVersionUID = 0;
042    
043        static final Matcher matchUrl = Pattern.compile("\\s*url\\((.*)\\)\\s*").matcher("");
044        static final Matcher matchFpNumUnits = Pattern.compile("\\s*([-+]?((\\d*\\.\\d+)|(\\d+))([-+]?[eE]\\d+)?)\\s*(px|cm|mm|in|pc|pt|em|ex)\\s*").matcher("");
045        
046        String name;
047        String stringValue;
048    
049        boolean colorCompatable = false;
050        boolean urlCompatable = false;
051    
052        /** Creates a new instance of StyleAttribute */
053        public StyleAttribute()
054        {
055            this(null, null);
056        }
057        
058        public StyleAttribute(String name) 
059        {
060            this.name = name;
061            stringValue = null;
062        }
063    
064        public StyleAttribute(String name, String stringValue) 
065        {
066            this.name = name;
067            this.stringValue = stringValue;
068        }
069    
070        public String getName() { return name; }
071        public StyleAttribute setName(String name) { this.name = name; return this; }
072        
073        public String getStringValue() { return stringValue; }
074    
075        public String[] getStringList() 
076        { 
077            return XMLParseUtil.parseStringList(stringValue);
078        }
079    
080        public void setStringValue(String value)
081        {
082            stringValue = value;
083        }
084    
085        public boolean getBooleanValue() {
086            return stringValue.toLowerCase().equals("true");
087        }
088    
089        public int getIntValue() {
090            return XMLParseUtil.findInt(stringValue);
091        }
092    
093        public int[] getIntList() {
094            return XMLParseUtil.parseIntList(stringValue);
095        }
096    
097        public double getDoubleValue() {
098            return XMLParseUtil.findDouble(stringValue);
099        }
100    
101        public double[] getDoubleList() {
102            return XMLParseUtil.parseDoubleList(stringValue);
103        }
104    
105        public float getFloatValue() {
106            return XMLParseUtil.findFloat(stringValue);
107        }
108    
109        public float[] getFloatList() {
110            return XMLParseUtil.parseFloatList(stringValue);
111        }
112    
113        public float getRatioValue() {
114            return (float)XMLParseUtil.parseRatio(stringValue);
115    //        try { return Float.parseFloat(stringValue); }
116    //        catch (Exception e) {}
117    //        return 0f;
118        }
119    
120        public String getUnits() {
121            matchFpNumUnits.reset(stringValue);
122            if (!matchFpNumUnits.matches()) return null;
123            return matchFpNumUnits.group(6);
124        }
125    
126        public NumberWithUnits getNumberWithUnits() {
127            return XMLParseUtil.parseNumberWithUnits(stringValue);
128        }
129    
130        public float getFloatValueWithUnits()
131        {
132            NumberWithUnits number = getNumberWithUnits();
133            return convertUnitsToPixels(number.getUnits(), number.getValue());
134        }
135        
136        static public float convertUnitsToPixels(int unitType, float value)
137        {
138            if (unitType == NumberWithUnits.UT_UNITLESS || unitType == NumberWithUnits.UT_PERCENT)
139            {
140                return value;
141            }
142            
143            float pixPerInch;
144            try 
145            {
146                pixPerInch = (float)Toolkit.getDefaultToolkit().getScreenResolution();
147            }
148            catch (HeadlessException ex)
149            {
150                //default to 72 dpi
151                pixPerInch = 72;
152            }
153            final float inchesPerCm = .3936f;
154    
155            switch (unitType)
156            {
157                case NumberWithUnits.UT_IN:
158                    return value * pixPerInch;
159                case NumberWithUnits.UT_CM:
160                    return value * inchesPerCm * pixPerInch;
161                case NumberWithUnits.UT_MM:
162                    return value * .1f * inchesPerCm * pixPerInch;
163                case NumberWithUnits.UT_PT:
164                    return value * (1f / 72f) * pixPerInch;
165                case NumberWithUnits.UT_PC:
166                    return value *  (1f / 6f) * pixPerInch;
167            }
168    
169            return value;
170        }
171    
172        public Color getColorValue()
173        {
174            return ColorTable.parseColor(stringValue);
175        }
176    
177        public String parseURLFn()
178        {
179            matchUrl.reset(stringValue);
180            if (!matchUrl.matches()) return null;
181            return matchUrl.group(1);
182        }
183    
184        public URL getURLValue(URL docRoot)
185        {
186            String fragment = parseURLFn();
187            if (fragment == null) return null;
188            try {
189                return new URL(docRoot, fragment);
190            }
191            catch (Exception e)
192            {
193                e.printStackTrace();
194                return null;
195            }
196        }
197    
198        public URL getURLValue(URI docRoot)
199        {
200            String fragment = parseURLFn();
201            if (fragment == null) return null;
202            try {
203                URI ref = docRoot.resolve(fragment);
204                return ref.toURL();
205            }
206            catch (Exception e)
207            {
208                e.printStackTrace();
209                return null;
210            }
211        }
212    
213        public URI getURIValue()
214        {
215            return getURIValue(null);
216        }
217        
218        /**
219         * Parse this sytle attribute as a URL and return it in URI form resolved
220         * against the passed base.
221         *
222         * @param base - URI to resolve against.  If null, will return value without
223         * attempting to resolve it.
224         */
225        public URI getURIValue(URI base)
226        {
227            try {
228                String fragment = parseURLFn();
229                if (fragment == null) fragment = stringValue.replaceAll("\\s+", "");
230                if (fragment == null) return null;
231                
232                //======================
233                //This gets around a bug in the 1.5.0 JDK
234                if (Pattern.matches("[a-zA-Z]:!\\\\.*", fragment))
235                {
236                    File file = new File(fragment);
237                    return file.toURI();
238                }
239                //======================
240    
241                //[scheme:]scheme-specific-part[#fragment]
242                
243                URI uriFrag = new URI(fragment);
244                if (uriFrag.isAbsolute())
245                {
246                    //Has scheme
247                    return uriFrag;
248                }
249            
250                if (base == null) return uriFrag;
251            
252                URI relBase = new URI(null, base.getSchemeSpecificPart(), null);
253                URI relUri;
254                if (relBase.isOpaque())
255                {
256                    relUri = new URI(null, base.getSchemeSpecificPart(), uriFrag.getFragment());
257                }
258                else
259                {
260                    relUri = relBase.resolve(uriFrag);
261                }
262                return new URI(base.getScheme() + ":" + relUri);
263            }
264            catch (Exception e)
265            {
266                e.printStackTrace();
267                return null;
268            }
269        }
270        
271        public static void main(String[] args)
272        {
273            try
274            {
275                URI uri = new URI("jar:http://www.kitfox.com/jackal/jackal.jar!/res/doc/about.svg");
276                uri = uri.resolve("#myFragment");
277                
278                System.err.println(uri.toString());
279                
280                uri = new URI("http://www.kitfox.com/jackal/jackal.html");
281                uri = uri.resolve("#myFragment");
282                
283                System.err.println(uri.toString());
284            }
285            catch (Exception e)
286            {
287                e.printStackTrace();
288            }
289        }
290    }