Give up after a longwinded and unfruitful attempt to patch ical4j, ICAL4J_EMBED_1

Fri, 13 Feb 2015 23:45:37 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 13 Feb 2015 23:45:37 +0100
branch
ICAL4J_EMBED_1
changeset 18
6dcaece8ec41
parent 15
cc93757aeca3
child 20
dd635af15958

Give up after a longwinded and unfruitful attempt to patch ical4j,
made difficult by the neverending graph of method calls leading up
to the NPE caused in getResource*() of the ResourceLoader class.

build.gradle file | annotate | diff | comparison | revisions
hack/model/TimeZoneRegistryImpl.java file | annotate | diff | comparison | revisions
hack/util/ResourceLoader.java file | annotate | diff | comparison | revisions
     1.1 --- a/build.gradle	Thu Feb 12 20:16:00 2015 +0100
     1.2 +++ b/build.gradle	Fri Feb 13 23:45:37 2015 +0100
     1.3 @@ -39,6 +39,38 @@
     1.4      }
     1.5      productFlavors {
     1.6      }
     1.7 +    //dexOptions {
     1.8 +    //    preDexLibraries = false
     1.9 +    //}
    1.10 +}
    1.11 +
    1.12 +// Kludgy attempt to patch ical4j
    1.13 +//task fixDeps(type: zip) {
    1.14 +//    zip -d $cache/ical4j-*.jar net/fortuna/ical4j/model/TimeZoneRegistryImpl.class
    1.15 +//    zip -d $cache/ical4j-*.jar net/fortuna/ical4j/util/ResourceLoader.class
    1.16 +//}
    1.17 +// Hack contains patched ical4j files
    1.18 +task patchIcal(type: Copy) {
    1.19 +    from 'hack'
    1.20 +    include 'model/'
    1.21 +    include 'util/'
    1.22 +    into 'src/net/fortuna/ical4j'
    1.23 +}
    1.24 +configure(patchIcal) {
    1.25 +    group = BasePlugin.ASSEMBLE_TASK_NAME // Or BUILD_GROUP
    1.26 +    description = 'Patch embedded ical4j dependency in a kludgy way'
    1.27 +}
    1.28 +task patchWipe(type: Delete) {
    1.29 +    delete 'src/net'
    1.30 +}
    1.31 +configure(patchWipe) {
    1.32 +    group = BasePlugin.ASSEMBLE_TASK_NAME // Or BUILD_GROUP
    1.33 +    description = 'Deletes patched dependencies in a kludgy way'
    1.34 +}
    1.35 +project.afterEvaluate{
    1.36 +    clean.dependsOn(patchWipe)
    1.37 +    compileDebugJava.dependsOn(patchIcal)
    1.38 +    compileReleaseJava.dependsOn(patchIcal)
    1.39  }
    1.40  
    1.41  dependencies {
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/hack/model/TimeZoneRegistryImpl.java	Fri Feb 13 23:45:37 2015 +0100
     2.3 @@ -0,0 +1,220 @@
     2.4 +/**
     2.5 + * Copyright (c) 2012, Ben Fortuna
     2.6 + * All rights reserved.
     2.7 + *
     2.8 + * Redistribution and use in source and binary forms, with or without
     2.9 + * modification, are permitted provided that the following conditions
    2.10 + * are met:
    2.11 + *
    2.12 + *  o Redistributions of source code must retain the above copyright
    2.13 + * notice, this list of conditions and the following disclaimer.
    2.14 + *
    2.15 + *  o Redistributions in binary form must reproduce the above copyright
    2.16 + * notice, this list of conditions and the following disclaimer in the
    2.17 + * documentation and/or other materials provided with the distribution.
    2.18 + *
    2.19 + *  o Neither the name of Ben Fortuna nor the names of any other contributors
    2.20 + * may be used to endorse or promote products derived from this software
    2.21 + * without specific prior written permission.
    2.22 + *
    2.23 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    2.24 + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    2.25 + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    2.26 + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
    2.27 + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
    2.28 + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
    2.29 + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
    2.30 + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
    2.31 + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
    2.32 + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    2.33 + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    2.34 + */
    2.35 +package net.fortuna.ical4j.model;
    2.36 +
    2.37 +import java.io.IOException;
    2.38 +import java.net.URL;
    2.39 +import java.util.Map;
    2.40 +import java.util.Properties;
    2.41 +import java.util.regex.Matcher;
    2.42 +import java.util.regex.Pattern;
    2.43 +
    2.44 +import net.fortuna.ical4j.data.CalendarBuilder;
    2.45 +import net.fortuna.ical4j.data.ParserException;
    2.46 +import net.fortuna.ical4j.model.component.VTimeZone;
    2.47 +import net.fortuna.ical4j.model.property.TzUrl;
    2.48 +import net.fortuna.ical4j.util.CompatibilityHints;
    2.49 +import net.fortuna.ical4j.util.Configurator;
    2.50 +import net.fortuna.ical4j.util.ResourceLoader;
    2.51 +
    2.52 +import android.util.Log;
    2.53 +
    2.54 +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap;
    2.55 +
    2.56 +/**
    2.57 + * $Id$
    2.58 + *
    2.59 + * Created on 18/09/2005
    2.60 + *
    2.61 + * The default implementation of a <code>TimeZoneRegistry</code>. This implementation will search the classpath for
    2.62 + * applicable VTimeZone definitions used to back the provided TimeZone instances.
    2.63 + * @author Ben Fortuna
    2.64 + */
    2.65 +public class TimeZoneRegistryImpl implements TimeZoneRegistry {
    2.66 +
    2.67 +    private static final String DEFAULT_RESOURCE_PREFIX = "zoneinfo/";
    2.68 +
    2.69 +    private static final Pattern TZ_ID_SUFFIX = Pattern.compile("(?<=/)[^/]*/[^/]*$");
    2.70 +    
    2.71 +    private static final String UPDATE_ENABLED = "net.fortuna.ical4j.timezone.update.enabled";
    2.72 +
    2.73 +    private static final Map DEFAULT_TIMEZONES = new ConcurrentHashMap();
    2.74 +
    2.75 +    private static final Properties ALIASES = new Properties();
    2.76 +    static {
    2.77 +        try {
    2.78 +            ALIASES.load(ResourceLoader.getResourceAsStream("tz.alias"));
    2.79 +        }
    2.80 +        catch (IOException ioe) {
    2.81 +			Log.w("MSvB-Hack", "Error loading timezone aliases: " + ioe.getMessage());
    2.82 +        }
    2.83 +        try {
    2.84 +        	ALIASES.load(ResourceLoader.getResourceAsStream("/tz.alias"));
    2.85 +        }
    2.86 +        catch (Exception e) {
    2.87 +			Log.d("MSvB-Hack", "Error loading custom timezone aliases: " + e.getMessage());
    2.88 +        }
    2.89 +    }
    2.90 +
    2.91 +    private Map timezones;
    2.92 +
    2.93 +    private String resourcePrefix;
    2.94 +
    2.95 +    /**
    2.96 +     * Default constructor.
    2.97 +     */
    2.98 +    public TimeZoneRegistryImpl() {
    2.99 +        this(DEFAULT_RESOURCE_PREFIX);
   2.100 +    }
   2.101 +
   2.102 +    /**
   2.103 +     * Creates a new instance using the specified resource prefix.
   2.104 +     * @param resourcePrefix a prefix prepended to classpath resource lookups for default timezones
   2.105 +     */
   2.106 +    public TimeZoneRegistryImpl(final String resourcePrefix) {
   2.107 +        this.resourcePrefix = resourcePrefix;
   2.108 +        timezones = new ConcurrentHashMap();
   2.109 +    }
   2.110 +
   2.111 +    /**
   2.112 +     * {@inheritDoc}
   2.113 +     */
   2.114 +    public final void register(final TimeZone timezone) {
   2.115 +    	// for now we only apply updates to included definitions by default..
   2.116 +    	register(timezone, false);
   2.117 +    }
   2.118 +    
   2.119 +    /**
   2.120 +     * {@inheritDoc}
   2.121 +     */
   2.122 +    public final void register(final TimeZone timezone, boolean update) {
   2.123 +    	if (update) {
   2.124 +            // load any available updates for the timezone..
   2.125 +            timezones.put(timezone.getID(), new TimeZone(updateDefinition(timezone.getVTimeZone())));
   2.126 +    	}
   2.127 +    	else {
   2.128 +            timezones.put(timezone.getID(), timezone);
   2.129 +    	}
   2.130 +    }
   2.131 +
   2.132 +    /**
   2.133 +     * {@inheritDoc}
   2.134 +     */
   2.135 +    public final void clear() {
   2.136 +        timezones.clear();
   2.137 +    }
   2.138 +
   2.139 +    /**
   2.140 +     * {@inheritDoc}
   2.141 +     */
   2.142 +    public final TimeZone getTimeZone(final String id) {
   2.143 +        TimeZone timezone = (TimeZone) timezones.get(id);
   2.144 +        if (timezone == null) {
   2.145 +            timezone = (TimeZone) DEFAULT_TIMEZONES.get(id);
   2.146 +            if (timezone == null) {
   2.147 +                // if timezone not found with identifier, try loading an alias..
   2.148 +                final String alias = ALIASES.getProperty(id);
   2.149 +                if (alias != null) {
   2.150 +                    return getTimeZone(alias);
   2.151 +                }
   2.152 +                else {
   2.153 +                    synchronized (DEFAULT_TIMEZONES) {
   2.154 +                    	// check again as it may be loaded now..
   2.155 +                    	timezone = (TimeZone) DEFAULT_TIMEZONES.get(id);
   2.156 +                    	if (timezone == null) {
   2.157 +                            try {
   2.158 +                                final VTimeZone vTimeZone = loadVTimeZone(id);
   2.159 +                                if (vTimeZone != null) {
   2.160 +                                    // XXX: temporary kludge..
   2.161 +                                    // ((TzId) vTimeZone.getProperties().getProperty(Property.TZID)).setValue(id);
   2.162 +                                    timezone = new TimeZone(vTimeZone);
   2.163 +                                    DEFAULT_TIMEZONES.put(timezone.getID(), timezone);
   2.164 +                                }
   2.165 +                                else if (CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_PARSING)) {
   2.166 +                                    // strip global part of id and match on default tz..
   2.167 +                                    Matcher matcher = TZ_ID_SUFFIX.matcher(id);
   2.168 +                                    if (matcher.find()) {
   2.169 +                                        return getTimeZone(matcher.group());
   2.170 +                                    }
   2.171 +                                }
   2.172 +                            }
   2.173 +                            catch (Exception e) {
   2.174 +                                Log.w("MSvB-Hack", "Error occurred loading VTimeZone", e);
   2.175 +                            }
   2.176 +                    	}
   2.177 +                    }
   2.178 +                }
   2.179 +            }
   2.180 +        }
   2.181 +        return timezone;
   2.182 +    }
   2.183 +
   2.184 +    /**
   2.185 +     * Loads an existing VTimeZone from the classpath corresponding to the specified Java timezone.
   2.186 +     */
   2.187 +    private VTimeZone loadVTimeZone(final String id) throws IOException, ParserException {
   2.188 +        final URL resource = ResourceLoader.getResource(resourcePrefix + id + ".ics");
   2.189 +        if (resource != null) {
   2.190 +            final CalendarBuilder builder = new CalendarBuilder();
   2.191 +            final Calendar calendar = builder.build(resource.openStream());
   2.192 +            final VTimeZone vTimeZone = (VTimeZone) calendar.getComponent(Component.VTIMEZONE);
   2.193 +            // load any available updates for the timezone.. can be explicility disabled via configuration
   2.194 +            if (!"false".equals(Configurator.getProperty(UPDATE_ENABLED))) {
   2.195 +                return updateDefinition(vTimeZone);
   2.196 +            }
   2.197 +            return vTimeZone;
   2.198 +        }
   2.199 +        return null;
   2.200 +    }
   2.201 +    
   2.202 +    /**
   2.203 +     * @param vTimeZone
   2.204 +     * @return
   2.205 +     */
   2.206 +    private VTimeZone updateDefinition(VTimeZone vTimeZone) {
   2.207 +        final TzUrl tzUrl = vTimeZone.getTimeZoneUrl();
   2.208 +        if (tzUrl != null) {
   2.209 +            try {
   2.210 +                final CalendarBuilder builder = new CalendarBuilder();
   2.211 +                final Calendar calendar = builder.build(tzUrl.getUri().toURL().openStream());
   2.212 +                final VTimeZone updatedVTimeZone = (VTimeZone) calendar.getComponent(Component.VTIMEZONE);
   2.213 +                if (updatedVTimeZone != null) {
   2.214 +                    return updatedVTimeZone;
   2.215 +                }
   2.216 +            }
   2.217 +            catch (Exception e) {
   2.218 +                Log.w("MSvB-Hack", "Unable to retrieve updates for timezone: " + vTimeZone.getTimeZoneId().getValue(), e);
   2.219 +            }
   2.220 +        }
   2.221 +        return vTimeZone;
   2.222 +    }
   2.223 +}
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/hack/util/ResourceLoader.java	Fri Feb 13 23:45:37 2015 +0100
     3.3 @@ -0,0 +1,101 @@
     3.4 +/**
     3.5 + * Copyright (c) 2012, Ben Fortuna
     3.6 + * All rights reserved.
     3.7 + *
     3.8 + * Redistribution and use in source and binary forms, with or without
     3.9 + * modification, are permitted provided that the following conditions
    3.10 + * are met:
    3.11 + *
    3.12 + *  o Redistributions of source code must retain the above copyright
    3.13 + * notice, this list of conditions and the following disclaimer.
    3.14 + *
    3.15 + *  o Redistributions in binary form must reproduce the above copyright
    3.16 + * notice, this list of conditions and the following disclaimer in the
    3.17 + * documentation and/or other materials provided with the distribution.
    3.18 + *
    3.19 + *  o Neither the name of Ben Fortuna nor the names of any other contributors
    3.20 + * may be used to endorse or promote products derived from this software
    3.21 + * without specific prior written permission.
    3.22 + *
    3.23 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    3.24 + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    3.25 + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    3.26 + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
    3.27 + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
    3.28 + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
    3.29 + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
    3.30 + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
    3.31 + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
    3.32 + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    3.33 + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    3.34 + */
    3.35 +package net.fortuna.ical4j.util;
    3.36 +
    3.37 +import java.io.InputStream;
    3.38 +import java.net.URL;
    3.39 +
    3.40 +import android.util.Log;
    3.41 +
    3.42 +/**
    3.43 + * @author fortuna
    3.44 + *
    3.45 + */
    3.46 +public class ResourceLoader {
    3.47 +
    3.48 +	/**
    3.49 +	 * Load a resource via the thread context classloader. If security permissions don't allow
    3.50 +	 * this fallback to loading via current classloader.
    3.51 +	 * @param name a resource name
    3.52 +	 * @return a {@link URL} or null if resource is not found
    3.53 +	 */
    3.54 +	public static URL getResource(String name) {
    3.55 +		URL resource = null;
    3.56 +		try {
    3.57 +			// Hack to bootstrap a multithreaded class loader context
    3.58 +			if (Thread.currentThread().getContextClassLoader() == null)
    3.59 +				Thread.currentThread().setContextClassLoader(ResourceLoader.class.getClassLoader());
    3.60 +			resource = Thread.currentThread().getContextClassLoader().getResource(name);
    3.61 +
    3.62 +			if (resource == null) // Flawed build path for assets, try again
    3.63 +				resource = Thread.currentThread().getContextClassLoader().getResource("/" + name);
    3.64 +		}
    3.65 +		catch (SecurityException e) {
    3.66 +			Log.i("MSvB-Hack", "Unable to access context classloader, using default. " + e.getMessage());
    3.67 +		}
    3.68 +		catch (Exception e) {
    3.69 +			Log.i("MSvB-Hack", "General context classloader error, using default. " + e.getMessage());
    3.70 +		}
    3.71 +		if (resource == null) {
    3.72 +			resource = ResourceLoader.class.getResource("/" + name);
    3.73 +		}
    3.74 +		return resource;
    3.75 +	}
    3.76 +
    3.77 +	/**
    3.78 +	 * Load a resource via the thread context classloader. If security permissions don't allow
    3.79 +	 * this fallback to loading via current classloader.
    3.80 +	 * @param name a resource name
    3.81 +	 * @return an {@link InputStream} or null if resource is not found
    3.82 +	 */
    3.83 +	public static InputStream getResourceAsStream(String name) {
    3.84 +		InputStream stream = null;
    3.85 +		try {
    3.86 +			// Hack to bootstrap a multithreaded class loader context
    3.87 +			if (Thread.currentThread().getContextClassLoader() == null)
    3.88 +				Thread.currentThread().setContextClassLoader(ResourceLoader.class.getClassLoader());
    3.89 +			stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(name);
    3.90 +			if (stream == null)
    3.91 +				stream = Thread.currentThread().getContextClassLoader().getResourceAsStream("/" + name);
    3.92 +		}
    3.93 +		catch (SecurityException e) {
    3.94 +			Log.i("MSvB-Hack", "Unable to access context classloader, using default. " + e.getMessage());
    3.95 +		}
    3.96 +		catch (Exception e) {
    3.97 +			Log.i("MSvB-Hack", "General context classloader error, using default. " + e.getMessage());
    3.98 +		}
    3.99 +		if (stream == null) {
   3.100 +			stream = ResourceLoader.class.getResourceAsStream("/" + name);
   3.101 +		}
   3.102 +		return stream;
   3.103 +	}
   3.104 +}

mercurial