001    // Copyright May 8, 2006 The Apache Software Foundation.
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    //     http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    package org.apache.tapestry.util;
015    
016    import org.apache.commons.lang.StringUtils;
017    import org.apache.commons.pool.KeyedPoolableObjectFactory;
018    import org.apache.commons.pool.impl.GenericKeyedObjectPool;
019    import org.apache.hivemind.ApplicationRuntimeException;
020    
021    import java.util.regex.Matcher;
022    import java.util.regex.Pattern;
023    
024    
025    /**
026     * Various scripting utility methods.
027     */
028    public final class ScriptUtils
029    {
030        /**
031         * XML cdata start.
032         */
033        public static final String BEGIN_COMMENT = "\n<script>\n//<![CDATA[\n";
034        /**
035         * XML character data end.
036         */
037        public static final String END_COMMENT = "\n//]]>\n</script>\n";
038    
039        /**
040         * Regexp represenging javascript matches.
041         */
042        public static final String SCRIPT_PATTERN = "(?:<script.*?>)(.*?)(?:<\\/script>)";
043    
044        private static final KeyedPoolableObjectFactory _factory = new RegexpPoolObjectFactory();
045    
046        private static final GenericKeyedObjectPool _pool;
047    
048        private static final int MAX_ACTIVE = 100;
049    
050        private static final long SLEEP_TIME = 1000 * 60 * 4;
051    
052        static {
053            _pool = new GenericKeyedObjectPool(_factory, MAX_ACTIVE, GenericKeyedObjectPool.WHEN_EXHAUSTED_BLOCK, -1);
054    
055            _pool.setMaxIdle(MAX_ACTIVE / 2);
056            _pool.setMinEvictableIdleTimeMillis(MAX_ACTIVE);
057            _pool.setTimeBetweenEvictionRunsMillis(SLEEP_TIME);
058        }
059    
060        /* defeat instantiation */
061        private ScriptUtils() { }
062    
063        /**
064         * Takes any <script>contents..</script> tags found in the specified
065         * input string and replaces their contents into one large <script></script>
066         * block (meaning if multiple script blocks are found, they will be turned into one),
067         * with the addition of {@link #BEGIN_COMMENT} inserted before the logic block and
068         * {@link #END_COMMENT} inserted after the logic block.
069         *
070         * @param input
071         *          The string to replace tags on
072         * @return The properly formatted string, if any formatting needed to occur.
073         */
074        public static String ensureValidScriptTags(String input) {
075    
076            if (input == null)
077                return null;
078    
079    
080            Pattern compiled = null;
081    
082            try {
083    
084                compiled = (Pattern)_pool.borrowObject(SCRIPT_PATTERN);
085    
086                Matcher matcher = compiled.matcher(input);
087                StringBuffer buffer = new StringBuffer(input.length());
088    
089                boolean matched = false;
090                int end = 0;
091                while (matcher.find()) {
092    
093                    matched = true;
094                    String str = matcher.group(1);
095                    int pos = matcher.start() - end;
096                    end = matcher.end();
097    
098                    if (str == null || str.trim().equals(""))
099                        matcher.appendReplacement(buffer, "");
100                    else {
101                        // gather the text from the beggining to the match into a new buffer
102                        StringBuffer matchLocal = new StringBuffer();
103                        matcher.appendReplacement(matchLocal, BEGIN_COMMENT + "$1" + END_COMMENT);
104    
105                        // the first part is always script-less, no need to remove comments from it.
106                        String curr =  matchLocal.toString();
107                        String prefix = curr.substring(0, pos);
108                        String suffix = curr.substring(pos);
109    
110                        // the second part is in a script, so remove comments.
111                        suffix = StringUtils.replace(suffix, "<!--", "");
112                        suffix = StringUtils.replace(suffix, "// -->", "");
113                        buffer.append(prefix).append(suffix);
114                    }
115                }
116    
117                if (!matched)
118                    buffer.append(input);
119                else {
120                    //copies non matched character input, ie content after the last script.
121                    matcher.appendTail(buffer);
122                }
123    
124                return buffer.toString();
125    
126            } catch (Exception e) {
127    
128                throw new ApplicationRuntimeException(e);
129            } finally {
130    
131                try { _pool.returnObject(SCRIPT_PATTERN, compiled); } catch (Throwable t) { }
132            }
133        }
134        
135        /**
136         * Utility that will attempt to generate a unique hash string
137         * that is javascript client in a function name based on the inomcing
138         * object's {@link Object#hashCode()} return value.
139         * 
140         * @param target The object to hash a string for.
141         * @return A string hash value, not necessarily exactly the same thing that would
142         *         be returned by {@link Object#hashCode()}.
143         */
144        public static String functionHash(Object target)
145        {
146            int hash = target.hashCode();
147            if (hash < 0) // flip exponent if negative
148                hash = hash*-1;
149            
150            return String.valueOf(hash);
151        }
152    }