diff options
Diffstat (limited to 'sched')
77 files changed, 2712 insertions, 1006 deletions
diff --git a/sched/.settings/findbugs-exclude.xml b/sched/.settings/findbugs-exclude.xml index 404658b..e21c2b2 100644 --- a/sched/.settings/findbugs-exclude.xml +++ b/sched/.settings/findbugs-exclude.xml @@ -23,6 +23,11 @@ </Match> <!-- See inlined comment in source file --> <Match> + <Class name="com.android.sched.util.log.stats.SampleImpl"/> + <Bug pattern="IS2_INCONSISTENT_SYNC"/> +</Match> +<!-- See inlined comment in source file --> +<Match> <Class name="com.android.sched.util.table.AbstractTable"/> <Bug pattern="EI_EXPOSE_REP"/> </Match> diff --git a/sched/.settings/org.eclipse.jdt.core.prefs b/sched/.settings/org.eclipse.jdt.core.prefs index 82380d3..34af015 100644 --- a/sched/.settings/org.eclipse.jdt.core.prefs +++ b/sched/.settings/org.eclipse.jdt.core.prefs @@ -188,7 +188,7 @@ org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false org.eclipse.jdt.core.formatter.comment.format_block_comments=true -org.eclipse.jdt.core.formatter.comment.format_header=true +org.eclipse.jdt.core.formatter.comment.format_header=false org.eclipse.jdt.core.formatter.comment.format_html=true org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true org.eclipse.jdt.core.formatter.comment.format_line_comments=true diff --git a/sched/src/com/android/sched/SchedProperties.java b/sched/src/com/android/sched/SchedProperties.java index c9edf55..5594905 100644 --- a/sched/src/com/android/sched/SchedProperties.java +++ b/sched/src/com/android/sched/SchedProperties.java @@ -16,6 +16,7 @@ package com.android.sched; +import com.android.sched.item.onlyfor.Default; import com.android.sched.item.onlyfor.OnlyForType; import com.android.sched.util.codec.ClassSelector; import com.android.sched.util.config.HasKeyId; @@ -32,10 +33,10 @@ public class SchedProperties { @Nonnull public static final BooleanPropertyId FAILED_STOP = BooleanPropertyId.create( "sched.failedstop", "Define if the SchedLib stop at the first failed") - .addDefaultValue("true"); + .addDefaultValue(Boolean.TRUE); @Nonnull public static final PropertyId<Class<? extends OnlyForType>> ONLY_FOR = PropertyId.create( "sched.onlyfor", "Define which items to take into account", - new ClassSelector<OnlyForType>(OnlyForType.class)).addDefaultValue("default"); + new ClassSelector<OnlyForType>(OnlyForType.class)).addDefaultValue(Default.class); } diff --git a/sched/src/com/android/sched/scheduler/MultiWorkersScheduleInstance.java b/sched/src/com/android/sched/scheduler/MultiWorkersScheduleInstance.java index de04742..64da80f 100644 --- a/sched/src/com/android/sched/scheduler/MultiWorkersScheduleInstance.java +++ b/sched/src/com/android/sched/scheduler/MultiWorkersScheduleInstance.java @@ -65,7 +65,7 @@ public class MultiWorkersScheduleInstance<T extends Component> "sched.runner.thread.synchronized", "If scheduler manages synchronized schedulable by itself").requiredIf( ScheduleInstance.DEFAULT_RUNNER.getClazz().isSubClassOf(MultiWorkersScheduleInstance.class)) - .addDefaultValue("true"); + .addDefaultValue(Boolean.TRUE); @Nonnull private static final IntegerPropertyId CHECK_FREQUENCY = IntegerPropertyId.create( diff --git a/sched/src/com/android/sched/scheduler/genetic/GeneticEventType.java b/sched/src/com/android/sched/scheduler/genetic/GeneticEventType.java index 049b935..a8777d5 100644 --- a/sched/src/com/android/sched/scheduler/genetic/GeneticEventType.java +++ b/sched/src/com/android/sched/scheduler/genetic/GeneticEventType.java @@ -25,24 +25,16 @@ import javax.annotation.Nonnull; * Represents a type of event whose performance is tracked */ enum GeneticEventType implements EventType { - ENGINE("Genetic engine", "Red"), - ANALYZER("Genetic analizing plan", "Red"), - BUILDER("Genetic builder builder", "Red"), - RANDOM_INIT("Random generator initializer", "Red"); - @Nonnull - private final String cssColor; + ENGINE("Genetic engine"), + ANALYZER("Genetic analizing plan"), + BUILDER("Genetic builder builder"), + RANDOM_INIT("Random generator initializer"); + @Nonnull private final String name; - GeneticEventType(@Nonnull String name, @Nonnull String cssColor) { + GeneticEventType(@Nonnull String name) { this.name = name; - this.cssColor = cssColor; - } - - @Override - @Nonnull - public String getColor() { - return cssColor; } @Override diff --git a/sched/src/com/android/sched/util/Colors.java b/sched/src/com/android/sched/util/Colors.java deleted file mode 100644 index 69c8372..0000000 --- a/sched/src/com/android/sched/util/Colors.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.sched.util; - -import java.awt.Color; -import java.util.Random; - -import javax.annotation.Nonnull; - -/** - * Utility class to manipulate Color. - */ -public class Colors { - /** - * Return a valid string describing a color valid for Css. - * @param color the color - * @return the string describing the color - */ - @Nonnull - public static String getCssColor(@Nonnull Color color) { - StringBuffer buffer = new StringBuffer(7); - buffer.append('#'); - buffer.append(getHexColorComponent(color.getRed())); - buffer.append(getHexColorComponent(color.getGreen())); - buffer.append(getHexColorComponent(color.getBlue())); - - return buffer.toString(); - } - - @Nonnull - private static String getHexColorComponent(int colorComponent) { - String hex = Integer.toString(colorComponent, 16); - - // Make sure hex value is two digits - if (hex.length() == 1) { - hex = "0" + hex; - } - - return hex; - } - - /** - * Create a random pastel color. - * - * @return a color - */ - @Nonnull - public static Color getRandomPastel() { - Random random = new Random(); - - final float hue = random.nextFloat(); - final float saturation = 0.9f; // 1.0 for brilliant, 0.0 for dull - final float luminance = 1.0f; // 1.0 for brighter, 0.0 for black - - return Color.getHSBColor(hue, saturation, luminance); - } - - /** - * Create a random pastel color based on a seed. - * - * @param seed the seed - * @return a color - */ - @Nonnull - public static Color getRandomPastel(int seed) { - final float hue; - if (seed != 0) { - hue = 1.0F / ((((long) seed) << 32) >>> 32); // Remove the sign - } else { - hue = 1.0F; - } - - final float saturation = 0.9f; // 1.0 for brilliant, 0.0 for dull - final float luminance = 1.0f; // 1.0 for brighter, 0.0 for black - - return Color.getHSBColor(hue, saturation, luminance); - } - - private Colors() {} -} diff --git a/sched/src/com/android/sched/util/ConcurrentIOException.java b/sched/src/com/android/sched/util/ConcurrentIOException.java new file mode 100644 index 0000000..b3ea109 --- /dev/null +++ b/sched/src/com/android/sched/util/ConcurrentIOException.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sched.util; + +import javax.annotation.Nonnull; + +/** + * Thrown when an external change to the file system prevents continuing the current processing. + */ +public class ConcurrentIOException extends UnrecoverableException { + + private static final long serialVersionUID = 1L; + + public ConcurrentIOException(@Nonnull Throwable cause) { + super(cause); + } + +} diff --git a/sched/src/com/android/sched/util/UnrecoverableException.java b/sched/src/com/android/sched/util/UnrecoverableException.java new file mode 100644 index 0000000..a3f2dff --- /dev/null +++ b/sched/src/com/android/sched/util/UnrecoverableException.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sched.util; + +import javax.annotation.Nonnull; + +/** + * Thrown when a major problem occurred because of an event out of our control. + * Handling this error should only be reporting to the user and maybe just retry exactly the same + * thing as the one that has thrown. + */ +public abstract class UnrecoverableException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public UnrecoverableException(@Nonnull Throwable cause) { + super(cause); + } + + @Override + public String getMessage() { + return getCause().getMessage(); + } +} diff --git a/sched/src/com/android/sched/util/codec/ClassSelector.java b/sched/src/com/android/sched/util/codec/ClassSelector.java index c8f45c5..f3f8cb9 100644 --- a/sched/src/com/android/sched/util/codec/ClassSelector.java +++ b/sched/src/com/android/sched/util/codec/ClassSelector.java @@ -18,6 +18,8 @@ package com.android.sched.util.codec; +import com.google.common.base.Joiner; + import com.android.sched.util.config.ConfigurationError; import javax.annotation.Nonnull; @@ -44,8 +46,8 @@ public class ClassSelector<T> extends Selector<T> implements StringCodec<Class<? public void checkValue(@Nonnull CodecContext context, @Nonnull Class<? extends T> cls) throws CheckingException { if (!checkClass(cls)) { - throw new CheckingException( - "The value must be " + getUsage() + " but is '" + formatValue(cls) + "'"); + throw new CheckingException("The value must be {" + Joiner.on(',').join(getClasses()) + + "} but is '" + cls.getCanonicalName() + "'"); } } diff --git a/sched/src/com/android/sched/util/codec/DefaultFactorySelector.java b/sched/src/com/android/sched/util/codec/DefaultFactorySelector.java index c357b9d..6a5c110 100644 --- a/sched/src/com/android/sched/util/codec/DefaultFactorySelector.java +++ b/sched/src/com/android/sched/util/codec/DefaultFactorySelector.java @@ -17,6 +17,8 @@ package com.android.sched.util.codec; +import com.google.common.base.Joiner; + import com.android.sched.util.config.ConfigurationError; import com.android.sched.util.config.DefaultFactory; import com.android.sched.util.config.ReflectDefaultCtorFactory; @@ -56,8 +58,9 @@ public class DefaultFactorySelector<T> extends Selector<T> public void checkValue(@Nonnull CodecContext context, @Nonnull DefaultFactory<T> factory) throws CheckingException { if (!checkClass(factory.getInstanciatedClass())) { - throw new CheckingException( - "The value must be " + getUsage() + " but is '" + formatValue(factory) + "'"); + throw new CheckingException("The value must be a DefaultFactory<{" + + Joiner.on(',').join(getClasses()) + "}> but is a DefaultFactory<" + + factory.getInstanciatedClass().getCanonicalName() + ">"); } } diff --git a/sched/src/com/android/sched/util/codec/DoubleCodec.java b/sched/src/com/android/sched/util/codec/DoubleCodec.java index 44ee717..a2be42f 100644 --- a/sched/src/com/android/sched/util/codec/DoubleCodec.java +++ b/sched/src/com/android/sched/util/codec/DoubleCodec.java @@ -72,7 +72,7 @@ public class DoubleCodec implements StringCodec<Double> { if (v < min || v > max) { throw new CheckingException( - "The value must be " + getUsage() + " but is '" + formatValue(d) + "'"); + "The value must be " + getUsage() + " but is " + d); } } diff --git a/sched/src/com/android/sched/util/codec/ImplementationSelector.java b/sched/src/com/android/sched/util/codec/ImplementationSelector.java index 1f06a15..e242be7 100644 --- a/sched/src/com/android/sched/util/codec/ImplementationSelector.java +++ b/sched/src/com/android/sched/util/codec/ImplementationSelector.java @@ -16,6 +16,8 @@ package com.android.sched.util.codec; +import com.google.common.base.Joiner; + import com.android.sched.util.config.ConfigurationError; import com.android.sched.util.config.ReflectDefaultCtorFactory; @@ -56,8 +58,9 @@ public class ImplementationSelector<T> extends Selector<T> implements StringCode public void checkValue(@Nonnull CodecContext context, @Nonnull T data) throws CheckingException { if (!checkClass((Class<? extends T>) data.getClass())) { - throw new CheckingException( - "The value must be " + getUsage() + " but is '" + formatValue(data) + "'"); + throw new CheckingException("The value must be an instance of {" + + Joiner.on(',').join(getClasses()) + "} but is an instance of '" + + data.getClass().getCanonicalName() + "'"); } } diff --git a/sched/src/com/android/sched/util/codec/KeyValueCodec.java b/sched/src/com/android/sched/util/codec/KeyValueCodec.java index ec9db66..4f16b47 100644 --- a/sched/src/com/android/sched/util/codec/KeyValueCodec.java +++ b/sched/src/com/android/sched/util/codec/KeyValueCodec.java @@ -20,6 +20,8 @@ import com.android.sched.util.config.ConfigurationError; import java.util.Arrays; import java.util.Comparator; +import java.util.HashSet; +import java.util.Set; import javax.annotation.Nonnull; @@ -109,8 +111,28 @@ public class KeyValueCodec<T> implements StringCodec<T> { } } - throw new CheckingException( - "The value must be " + getUsage() + " but is '" + formatValue(value) + "'"); + Set<T> set = new HashSet<T>(); + for (Entry<T> entry : entries) { + set.add(entry.value); + } + + StringBuilder sb = new StringBuilder(); + boolean first = true; + for (T data : set) { + if (first) { + first = false; + } else { + sb.append(", "); + } + + sb.append(data); + sb.append(" ("); + sb.append(data.getClass().getCanonicalName()); + sb.append(')'); + } + + throw new CheckingException("The value must be {" + sb.toString() + "} but is '" + value + " (" + + value.getClass().getCanonicalName() + ")'"); } @Override diff --git a/sched/src/com/android/sched/util/codec/LongCodec.java b/sched/src/com/android/sched/util/codec/LongCodec.java index 4c4153c..cf9b8da 100644 --- a/sched/src/com/android/sched/util/codec/LongCodec.java +++ b/sched/src/com/android/sched/util/codec/LongCodec.java @@ -76,7 +76,7 @@ public class LongCodec implements StringCodec<Long>{ if (v < min || v > max) { throw new CheckingException( - "The value must be " + getUsage() + " but is '" + formatValue(l) + "'"); + "The value must be " + getUsage() + " but is " + l); } } diff --git a/sched/src/com/android/sched/util/codec/ReflectFactorySelector.java b/sched/src/com/android/sched/util/codec/ReflectFactorySelector.java index 43819f7..3ea1073 100644 --- a/sched/src/com/android/sched/util/codec/ReflectFactorySelector.java +++ b/sched/src/com/android/sched/util/codec/ReflectFactorySelector.java @@ -16,6 +16,8 @@ package com.android.sched.util.codec; +import com.google.common.base.Joiner; + import com.android.sched.util.config.ConfigurationError; import com.android.sched.util.config.ReflectFactory; @@ -78,8 +80,9 @@ public class ReflectFactorySelector<T> public void checkValue(@Nonnull CodecContext context, @Nonnull ReflectFactory<T> factory) throws CheckingException { if (!checkClass(factory.getInstanciatedClass())) { - throw new CheckingException( - "The value must be " + getUsage() + " but is '" + formatValue(factory) + "'"); + throw new CheckingException("The value must be a ReflectFactory<{" + + Joiner.on(',').join(getClasses()) + "}> but is a ReflectFactory<" + + factory.getInstanciatedClass().getCanonicalName() + ">"); } } diff --git a/sched/src/com/android/sched/util/codec/Selector.java b/sched/src/com/android/sched/util/codec/Selector.java index 799f106..3e4d90e 100644 --- a/sched/src/com/android/sched/util/codec/Selector.java +++ b/sched/src/com/android/sched/util/codec/Selector.java @@ -27,6 +27,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -85,6 +86,7 @@ public abstract class Selector<T> { public String getName(@Nonnull Class<? extends T> type) { ensureScan(); assert propertyValues != null; + for (Entry<String, Class<? extends T>> entry : propertyValues.entrySet()) { if (entry.getValue() == type) { return entry.getKey(); @@ -127,6 +129,17 @@ public abstract class Selector<T> { return list; } + @Nonnull + public Set<Class<? extends T>> getClasses() { + ensureScan(); + assert propertyValues != null; + + Set<Class<? extends T>> set = new HashSet<Class<? extends T>>(); + set.addAll(propertyValues.values()); + + return set; + } + private synchronized void ensureScan() { if (propertyValues == null) { propertyValues = new HashMap<String, Class<? extends T>>(); diff --git a/sched/src/com/android/sched/util/config/AsapConfigBuilder.java b/sched/src/com/android/sched/util/config/AsapConfigBuilder.java index 0b8bdf2..597d44e 100644 --- a/sched/src/com/android/sched/util/config/AsapConfigBuilder.java +++ b/sched/src/com/android/sched/util/config/AsapConfigBuilder.java @@ -38,6 +38,7 @@ import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; @@ -69,11 +70,11 @@ public class AsapConfigBuilder { new HashMap<KeyId<?, ?>, FieldLocation>(); @Nonnull - private final Map<PropertyId<?>, String> stringValuesById = - new HashMap<PropertyId<?>, String>(); + private final Map<PropertyId<?>, PropertyId<?>.Value> valuesById = + new HashMap<PropertyId<?>, PropertyId<?>.Value>(); @Nonnull - private final Map<ObjectId<?>, Object> instanceValuesById = + private final Map<ObjectId<?>, Object> instances = new HashMap<ObjectId<?>, Object>(); @Nonnull @@ -182,13 +183,7 @@ public class AsapConfigBuilder { } @Nonnull - public AsapConfigBuilder set(@Nonnull String name, @Nonnull String value) - throws UnknownPropertyNameException, PropertyIdException { - return set(name, value, defaultLocations.peek()); - } - - @Nonnull - public AsapConfigBuilder set( + public AsapConfigBuilder setString( @Nonnull String name, @Nonnull String value, @Nonnull Location location) throws UnknownPropertyNameException, PropertyIdException { KeyId<?, ?> keyId = keyIdsByName.get(name); @@ -197,7 +192,7 @@ public class AsapConfigBuilder { } try { - set((PropertyId<?>) keyId, value, location); + setString((PropertyId<?>) keyId, value, location); } catch (UnknownPropertyIdException e) { throw new AssertionError(); } @@ -206,13 +201,36 @@ public class AsapConfigBuilder { } @Nonnull - public AsapConfigBuilder set(@Nonnull PropertyId<?> propertyId, @Nonnull String value) - throws PropertyIdException { - return set(propertyId, value, defaultLocations.peek()); + public <T> AsapConfigBuilder set( + @Nonnull String name, @Nonnull T value, @Nonnull Location location) + throws UnknownPropertyNameException, PropertyIdException { + KeyId<?, ?> keyId = keyIdsByName.get(name); + if (keyId == null || !(keyId instanceof PropertyId)) { + throw new UnknownPropertyNameException(name); + } + + @SuppressWarnings("unchecked") + PropertyId<T> propertyId = (PropertyId<T>) keyId; + + if (context.isDebug()) { + try { + propertyId.getCodec().checkValue(context, value); + } catch (Exception e) { + throw new ConfigurationError("Property '" + name + "': " + e.getMessage()); + } + } + + try { + set(propertyId, value, location); + } catch (UnknownPropertyIdException e) { + throw new AssertionError(); + } + + return this; } @Nonnull - public AsapConfigBuilder set( + public AsapConfigBuilder setString( @Nonnull PropertyId<?> propertyId, @Nonnull String value, @Nonnull Location location) throws PropertyIdException { if (!keyIdsByName.values().contains(propertyId)) { @@ -227,21 +245,39 @@ public class AsapConfigBuilder { } } - stringValuesById.put(propertyId, value); + valuesById.put(propertyId, propertyId.new Value(value)); locationsByKeyId.put(propertyId, location); return this; } + @Nonnull - public <T> AsapConfigBuilder set(@Nonnull ObjectId<T> objectId, @Nonnull T value) { - return set(objectId, value, defaultLocations.peek()); + public <T> AsapConfigBuilder set( + @Nonnull PropertyId<T> propertyId, @Nonnull T value, @Nonnull Location location) + throws PropertyIdException { + if (!keyIdsByName.values().contains(propertyId)) { + throw new UnknownPropertyIdException(propertyId); + } + + if (context.isDebug()) { + try { + propertyId.getCodec().checkValue(context, value); + } catch (Exception e) { + throw new ConfigurationError("Property '" + propertyId.getName() + "': " + e.getMessage()); + } + } + + valuesById.put(propertyId, propertyId.new Value(value)); + locationsByKeyId.put(propertyId, location); + + return this; } @Nonnull public <T> AsapConfigBuilder set( @Nonnull ObjectId<T> objectId, @Nonnull T value, @Nonnull Location location) { - instanceValuesById.put(objectId, value); + instances.put(objectId, value); locationsByKeyId.put(objectId, location); return this; @@ -276,7 +312,7 @@ public class AsapConfigBuilder { * @throws ConfigurationException */ @Nonnull - public Config build() throws ConfigurationException { + public <X> Config build() throws ConfigurationException { ChainedExceptionBuilder<ConfigurationException> exceptions = new ChainedExceptionBuilder<ConfigurationException>(); @@ -286,12 +322,14 @@ public class AsapConfigBuilder { logger.setLevel(Level.INFO); } - @Nonnull Map<PropertyId<?>, String> values = new HashMap<PropertyId<?>, String>(); + @Nonnull + Map<PropertyId<?>, PropertyId<?>.Value> values = + new HashMap<PropertyId<?>, PropertyId<?>.Value>(); processValues(values); processDefaultValues(values); ConfigChecker checker = - new ConfigChecker(context, values, instanceValuesById, locationsByKeyId); + new ConfigChecker(context, values, instances, locationsByKeyId); for (KeyId<?, ?> keyId : keyIdsByName.values()) { boolean needChecks = false; @@ -343,15 +381,15 @@ public class AsapConfigBuilder { if (context.isDebug()) { return new ConfigDebug( - context, checker.getStrings(), checker.getInstances(), checker.getDropCauses()); + context, checker.getValues(), checker.getInstances(), checker.getDropCauses()); } else { - return new ConfigImpl(context, checker.getStrings(), checker.getInstances()); + return new ConfigImpl(context, checker.getValues(), checker.getInstances()); } } @Nonnull public Collection<PropertyId<?>> getPropertyIds() { - ArrayList<PropertyId<?>> result = new ArrayList<PropertyId<?>>(keyIdsByName.size()); + List<PropertyId<?>> result = new ArrayList<PropertyId<?>>(keyIdsByName.size()); for (KeyId<?, ?> keyId : keyIdsByName.values()) { if (keyId.isPublic() && keyId instanceof PropertyId<?>) { @@ -363,15 +401,21 @@ public class AsapConfigBuilder { } @CheckForNull - public String getDefaultValue(@Nonnull PropertyId<?> propertyId) { - return propertyId.getDefaultValue(context); + public <T> String getDefaultValue(@Nonnull PropertyId<T> propertyId) { + PropertyId<T>.Value value = propertyId.getDefaultValue(context); + + if (value != null) { + return value.getString(); + } else { + return null; + } } - private void processValues(@Nonnull Map<PropertyId<?>, String> values) { - values.putAll(stringValuesById); + private void processValues(@Nonnull Map<PropertyId<?>, PropertyId<?>.Value> values) { + values.putAll(valuesById); } - private void processDefaultValues(@Nonnull Map<PropertyId<?>, String> values) { + private void processDefaultValues(@Nonnull Map<PropertyId<?>, PropertyId<?>.Value> values) { for (KeyId<?, ?> keyId : keyIdsByName.values()) { if (keyId instanceof PropertyId) { PropertyId<?> propertyId = (PropertyId<?>) keyId; @@ -427,7 +471,7 @@ public class AsapConfigBuilder { try { assert propertyId != null; - set(propertyId, value, new EnvironmentLocation(envKey)); + setString(propertyId, value, new EnvironmentLocation(envKey)); } catch (ConfigurationException e) { exceptions.appendException(e); } @@ -450,6 +494,10 @@ public class AsapConfigBuilder { return this; } + // + // Default location + // + public void pushDefaultLocation(@Nonnull Location location) { defaultLocations.push(location); } @@ -458,4 +506,221 @@ public class AsapConfigBuilder { assert defaultLocations.size() > 1; defaultLocations.pop(); } + + @Nonnull + public <T> AsapConfigBuilder set(@Nonnull ObjectId<T> objectId, @Nonnull T value) { + return set(objectId, value, defaultLocations.peek()); + } + + @Nonnull + public <T> AsapConfigBuilder set(@Nonnull String name, @Nonnull T value) + throws UnknownPropertyNameException, PropertyIdException { + return set(name, value, defaultLocations.peek()); + } + + @Nonnull + public <T> AsapConfigBuilder set(@Nonnull PropertyId<T> propertyId, @Nonnull T value) + throws PropertyIdException { + return set(propertyId, value, defaultLocations.peek()); + } + + @Nonnull + public AsapConfigBuilder setString(@Nonnull PropertyId<?> propertyId, @Nonnull String value) + throws PropertyIdException { + return setString(propertyId, value, defaultLocations.peek()); + } + + @Nonnull + public AsapConfigBuilder setString(@Nonnull String name, @Nonnull String value) + throws UnknownPropertyNameException, PropertyIdException { + return setString(name, value, defaultLocations.peek()); + } + + // + // Commodity helper + // + + @Nonnull + public AsapConfigBuilder set(@Nonnull PropertyId<Boolean> propertyId, boolean value) { + try { + set(propertyId, Boolean.valueOf(value)); + } catch (PropertyIdException e) { + throw new AssertionError(); + } + + return this; + } + + @Nonnull + public AsapConfigBuilder set(@Nonnull PropertyId<Boolean> propertyId, boolean value, + @Nonnull Location location) { + try { + set(propertyId, Boolean.valueOf(value), location); + } catch (PropertyIdException e) { + throw new AssertionError(); + } + + return this; + } + + @Nonnull + public AsapConfigBuilder set(@Nonnull PropertyId<Byte> propertyId, byte value) { + try { + set(propertyId, Byte.valueOf(value)); + } catch (PropertyIdException e) { + throw new AssertionError(); + } + + return this; + } + + @Nonnull + public AsapConfigBuilder set(@Nonnull PropertyId<Byte> propertyId, byte value, + @Nonnull Location location) { + try { + set(propertyId, Byte.valueOf(value), location); + } catch (PropertyIdException e) { + throw new AssertionError(); + } + + return this; + } + + @Nonnull + public AsapConfigBuilder set(@Nonnull PropertyId<Short> propertyId, short value) { + try { + set(propertyId, Short.valueOf(value)); + } catch (PropertyIdException e) { + throw new AssertionError(); + } + + return this; + } + + @Nonnull + public AsapConfigBuilder set(@Nonnull PropertyId<Short> propertyId, short value, + @Nonnull Location location) { + try { + set(propertyId, Short.valueOf(value), location); + } catch (PropertyIdException e) { + throw new AssertionError(); + } + + return this; + } + + @Nonnull + public AsapConfigBuilder set(@Nonnull PropertyId<Character> propertyId, char value) { + try { + set(propertyId, Character.valueOf(value)); + } catch (PropertyIdException e) { + throw new AssertionError(); + } + + return this; + } + + @Nonnull + public AsapConfigBuilder set(@Nonnull PropertyId<Character> propertyId, char value, + @Nonnull Location location) { + try { + set(propertyId, Character.valueOf(value), location); + } catch (PropertyIdException e) { + throw new AssertionError(); + } + + return this; + } + + @Nonnull + public AsapConfigBuilder set(@Nonnull PropertyId<Integer> propertyId, int value) { + try { + set(propertyId, Integer.valueOf(value)); + } catch (PropertyIdException e) { + throw new AssertionError(); + } + + return this; + } + + @Nonnull + public AsapConfigBuilder set(@Nonnull PropertyId<Integer> propertyId, int value, + @Nonnull Location location) { + try { + set(propertyId, Integer.valueOf(value), location); + } catch (PropertyIdException e) { + throw new AssertionError(); + } + + return this; + } + + @Nonnull + public AsapConfigBuilder set(@Nonnull PropertyId<Long> propertyId, long value) { + try { + set(propertyId, Long.valueOf(value)); + } catch (PropertyIdException e) { + throw new AssertionError(); + } + + return this; + } + + @Nonnull + public AsapConfigBuilder set(@Nonnull PropertyId<Long> propertyId, long value, + @Nonnull Location location) { + try { + set(propertyId, Long.valueOf(value), location); + } catch (PropertyIdException e) { + throw new AssertionError(); + } + + return this; + } + + @Nonnull + public AsapConfigBuilder set(@Nonnull PropertyId<Float> propertyId, float value) { + try { + set(propertyId, Float.valueOf(value)); + } catch (PropertyIdException e) { + throw new AssertionError(); + } + + return this; + } + + @Nonnull + public AsapConfigBuilder set(@Nonnull PropertyId<Float> propertyId, float value, + @Nonnull Location location) { + try { + set(propertyId, Float.valueOf(value), location); + } catch (PropertyIdException e) { + throw new AssertionError(); + } + + return this; + } + + @Nonnull + public AsapConfigBuilder set(@Nonnull PropertyId<Double> propertyId, double value) { + try { + set(propertyId, Double.valueOf(value)); + } catch (PropertyIdException e) { + throw new AssertionError(); + } + + return this; + } + + @Nonnull + public AsapConfigBuilder set(@Nonnull PropertyId<Double> propertyId, double value, + @Nonnull Location location) { + try { + set(propertyId, Double.valueOf(value), location); + } catch (PropertyIdException e) { + throw new AssertionError(); + } + + return this; + } } diff --git a/sched/src/com/android/sched/util/config/Config.java b/sched/src/com/android/sched/util/config/Config.java index 40faa5c..2bbf884 100644 --- a/sched/src/com/android/sched/util/config/Config.java +++ b/sched/src/com/android/sched/util/config/Config.java @@ -17,18 +17,22 @@ package com.android.sched.util.config; import com.android.sched.util.config.id.KeyId; +import com.android.sched.util.config.id.ObjectId; import com.android.sched.util.config.id.PropertyId; import java.util.Collection; import javax.annotation.Nonnull; - /** - * The class that deals with configuration properties in 'get' mode. + * The interface that deals with configuration properties in 'get' mode. */ public interface Config { @Nonnull + public <T> T get(@Nonnull PropertyId<T> propertyId); + @Nonnull + public <T> T get(@Nonnull ObjectId<T> objectId); + @Nonnull public <T, S> T get(@Nonnull KeyId<T, S> keyId); @Nonnull public <T> String getAsString(@Nonnull PropertyId<T> propertyId); diff --git a/sched/src/com/android/sched/util/config/ConfigChecker.java b/sched/src/com/android/sched/util/config/ConfigChecker.java index 4f151c8..781d77a 100644 --- a/sched/src/com/android/sched/util/config/ConfigChecker.java +++ b/sched/src/com/android/sched/util/config/ConfigChecker.java @@ -18,10 +18,10 @@ package com.android.sched.util.config; import com.android.sched.util.codec.CodecContext; import com.android.sched.util.codec.ParsingException; -import com.android.sched.util.codec.StringCodec; import com.android.sched.util.config.id.KeyId; import com.android.sched.util.config.id.ObjectId; import com.android.sched.util.config.id.PropertyId; +import com.android.sched.util.config.id.PropertyId.Value; import java.util.HashMap; import java.util.Map; @@ -35,14 +35,15 @@ public class ConfigChecker { @Nonnull private final CodecContext context; @Nonnull - private final Map<PropertyId<?>, String> stringValuesById = new HashMap<PropertyId<?>, String>(); + private final Map<PropertyId<?>, PropertyId<?>.Value> values = + new HashMap<PropertyId<?>, PropertyId<?>.Value>(); @Nonnull - private final Map<KeyId<?, ?>, Object> instanceValuesById = new HashMap<KeyId<?, ?>, Object>(); + private final Map<KeyId<?, ?>, Object> instances = new HashMap<KeyId<?, ?>, Object>(); @Nonnull - private final Map<KeyId<?, ?>, Location> locationsById = + private final Map<KeyId<?, ?>, Location> locations = new HashMap<KeyId<?, ?>, Location>(); @Nonnull - private final Map<KeyId<?, ?>, String> droppedById = new HashMap<KeyId<?, ?>, String>(); + private final Map<KeyId<?, ?>, String> dropped = new HashMap<KeyId<?, ?>, String>(); /** * @param context Context for parsers @@ -50,57 +51,50 @@ public class ConfigChecker { * @param instanceValues All the property values as objects. */ ConfigChecker(@Nonnull CodecContext context, - @Nonnull Map<PropertyId<?>, String> stringValues, + @Nonnull Map<PropertyId<?>, PropertyId<?>.Value> stringValues, @Nonnull Map<ObjectId<?>, Object> instanceValues, @Nonnull Map<KeyId<?, ?>, Location> locationsById) { this.context = context; - this.stringValuesById.putAll(stringValues); - this.instanceValuesById.putAll(instanceValues); - this.locationsById.putAll(locationsById); + this.values.putAll(stringValues); + this.instances.putAll(instanceValues); + this.locations.putAll(locationsById); } @Nonnull - public synchronized <T> T parse(@Nonnull PropertyId<T> propertyId) throws PropertyIdException - { - @SuppressWarnings("unchecked") - T instance = (T) instanceValuesById.get(propertyId); - - if (instance == null) { - String value = getRawValue(propertyId); - try { - StringCodec<T> parser = propertyId.getCodec(); - - instance = parser.checkString(context, value); - if (instance == null) { - instance = parser.parseString(context, value); - } + public synchronized <T> T parse(@Nonnull PropertyId<T> propertyId) throws PropertyIdException { + @SuppressWarnings({"unchecked", "rawtypes"}) + PropertyId<T>.Value value = (PropertyId.Value) values.get(propertyId); - instanceValuesById.put(propertyId, instance); - stringValuesById.remove(propertyId); - } catch (ParsingException e) { - throw new PropertyIdException(propertyId, getLocation(propertyId), e); - } + if (value == null) { + throw new MissingPropertyException(propertyId); } - return instance; + try { + value.check(context); + return value.getObject(context); + } catch (ParsingException e) { + throw new PropertyIdException(propertyId, getLocation(propertyId), e); + } } public synchronized <T, S> void check(@Nonnull KeyId<T, S> keyId) throws PropertyIdException { - if (instanceValuesById.get(keyId) == null) { + if (instances.get(keyId) == null) { if (keyId instanceof PropertyId) { @SuppressWarnings("unchecked") PropertyId<T> propertyId = (PropertyId<T>) keyId; - String value = getRawValue(propertyId); + + @SuppressWarnings({"unchecked", "rawtypes"}) + PropertyId<T>.Value value = (PropertyId.Value) values.get(propertyId); + + if (value == null) { + throw new MissingPropertyException(propertyId); + } + try { - T instance = propertyId.getCodec().checkString(context, value); - if (instance != null) { - instanceValuesById.put(propertyId, instance); - stringValuesById.remove(keyId); - } + value.check(context); } catch (ParsingException e) { throw new PropertyIdException(propertyId, getLocation(propertyId), e); } - } else { assert keyId instanceof ObjectId; @@ -114,41 +108,42 @@ public class ConfigChecker { @Nonnull public <T> String getRawValue(@Nonnull PropertyId<T> propertyId) throws MissingPropertyException { - String value = stringValuesById.get(propertyId); + @SuppressWarnings({"rawtypes", "unchecked"}) + PropertyId<T>.Value value = (Value) values.get(propertyId); if (value == null) { throw new MissingPropertyException(propertyId); } - return value; + return value.getString(); } @Nonnull public Map<KeyId<?, ?>, Object> getInstances() { - return instanceValuesById; + return instances; } @Nonnull - public Map<PropertyId<?>, String> getStrings() { - return stringValuesById; + public Map<PropertyId<?>, PropertyId<?>.Value> getValues() { + return values; } @Nonnull public Map<KeyId<?, ?>, String> getDropCauses() { - return droppedById; + return dropped; } @Nonnull public Location getLocation(@Nonnull KeyId<?, ?> keyId) { - assert locationsById.get(keyId) != null; + assert locations.get(keyId) != null; - return locationsById.get(keyId); + return locations.get(keyId); } public void remove(@Nonnull KeyId<?, ?> keyId, @Nonnull String cause) { - stringValuesById.remove(keyId); - instanceValuesById.remove(keyId); - locationsById.remove(keyId); - droppedById.put(keyId, cause); + values.remove(keyId); + instances.remove(keyId); + locations.remove(keyId); + dropped.put(keyId, cause); } } diff --git a/sched/src/com/android/sched/util/config/ConfigDebug.java b/sched/src/com/android/sched/util/config/ConfigDebug.java index f7b5a94..db926d1 100644 --- a/sched/src/com/android/sched/util/config/ConfigDebug.java +++ b/sched/src/com/android/sched/util/config/ConfigDebug.java @@ -18,6 +18,7 @@ package com.android.sched.util.config; import com.android.sched.util.codec.CodecContext; import com.android.sched.util.config.id.KeyId; +import com.android.sched.util.config.id.ObjectId; import com.android.sched.util.config.id.PropertyId; import com.android.sched.util.log.LoggerFactory; @@ -47,16 +48,29 @@ class ConfigDebug extends ConfigImpl { } }; - ConfigDebug(@Nonnull CodecContext context, @Nonnull Map<PropertyId<?>, String> stringValues, - @Nonnull Map<KeyId<?, ?>, Object> instanceValues, + ConfigDebug(@Nonnull CodecContext context, + @Nonnull Map<PropertyId<?>, PropertyId<?>.Value> values, + @Nonnull Map<KeyId<?, ?>, Object> instances, @Nonnull Map<KeyId<?, ?>, String> dropCauses) { - super(context, stringValues, instanceValues); + super(context, values, instances); this.dropCauses = new HashMap<KeyId<?, ?>, String>(dropCauses); } @Override @Nonnull + public synchronized <T> T get(@Nonnull PropertyId<T> propertyId) { + return get((KeyId<T, ?>) propertyId); + } + + @Override + @Nonnull + public synchronized <T> T get(@Nonnull ObjectId<T> objectId) { + return get((KeyId<T, ?>) objectId); + } + + @Override + @Nonnull public <T, S> T get(@Nonnull KeyId<T, S> keyId) { Stack<KeyId<?, ?>> localKeyIds = keyIds.get(); diff --git a/sched/src/com/android/sched/util/config/ConfigImpl.java b/sched/src/com/android/sched/util/config/ConfigImpl.java index 00a1bce..d3d6ee9 100644 --- a/sched/src/com/android/sched/util/config/ConfigImpl.java +++ b/sched/src/com/android/sched/util/config/ConfigImpl.java @@ -26,71 +26,110 @@ import java.util.Collection; import java.util.HashMap; import java.util.Map; +import javax.annotation.CheckForNull; import javax.annotation.Nonnull; /** * Implementation of a fully built {@code Config}. */ -class ConfigImpl implements Config { +class ConfigImpl implements Config, InternalConfig { @Nonnull private final CodecContext context; @Nonnull - private final Map<PropertyId<?>, String> stringValuesById = new HashMap<PropertyId<?>, String>(); + private final Map<PropertyId<?>, PropertyId<?>.Value> valuesById = + new HashMap<PropertyId<?>, PropertyId<?>.Value>(); @Nonnull - private final Map<KeyId<?, ?>, Object> instanceValuesById = new HashMap<KeyId<?, ?>, Object>(); + private final Map<KeyId<?, ?>, Object> instancesById = new HashMap<KeyId<?, ?>, Object>(); /** * @param context Context for parsers - * @param stringValues All the property values as {@code String} objects. - * @param instanceValues All the property values as objects. + * @param values All the property values as {@code String} objects. + * @param instances All the property values as objects. */ - ConfigImpl(@Nonnull CodecContext context, @Nonnull Map<PropertyId<?>, String> stringValues, - @Nonnull Map<KeyId<?, ?>, Object> instanceValues) { + ConfigImpl(@Nonnull CodecContext context, @Nonnull Map<PropertyId<?>, PropertyId<?>.Value> values, + @Nonnull Map<KeyId<?, ?>, Object> instances) { this.context = context; - this.stringValuesById.putAll(stringValues); - this.instanceValuesById.putAll(instanceValues); + this.valuesById.putAll(values); + this.instancesById.putAll(instances); } @Override @Nonnull - public synchronized <T, S> T get(@Nonnull KeyId<T, S> keyId) { - @SuppressWarnings("unchecked") - T instance = (T) instanceValuesById.get(keyId); + public <T> T get(@Nonnull PropertyId<T> propertyId) { + @SuppressWarnings({"unchecked", "rawtypes"}) + PropertyId<T>.Value value = (PropertyId.Value) valuesById.get(propertyId); + + if (value == null) { + throw new ConfigurationError("Property '" + propertyId.getName() + + "' is unknown (see annotation @" + HasKeyId.class.getSimpleName() + + " or requiredIf expression)"); + } - if (instance == null) { - if (keyId instanceof PropertyId) { - @SuppressWarnings("unchecked") - PropertyId<T> propertyId = (PropertyId<T>) keyId; - - String value = stringValuesById.get(propertyId); - if (value == null) { - throw new ConfigurationError("Property '" + propertyId.getName() - + "' is unknown (see annotation @" + HasKeyId.class.getSimpleName() - + " or requiredIf expression)"); - } + return value.getObject(context); + } - instance = propertyId.getCodec().parseString(context, value); - instanceValuesById.put(propertyId, instance); - stringValuesById.remove(propertyId); - } else { - @SuppressWarnings("unchecked") - ObjectId<T> objectId = (ObjectId<T>) keyId; + @Override + @CheckForNull + public <T> T getObjectIfAny(@Nonnull PropertyId<T> propertyId) { + @SuppressWarnings({"unchecked", "rawtypes"}) + PropertyId<T>.Value value = (PropertyId.Value) valuesById.get(propertyId); + + if (value == null) { + throw new ConfigurationError("Property '" + propertyId.getName() + + "' is unknown (see annotation @" + HasKeyId.class.getSimpleName() + + " or requiredIf expression)"); + } - instance = objectId.createObject(); - instanceValuesById.put(objectId, instance); - } + return value.getObjectIfAny(); + } + + @Override + @Nonnull + public <T> String getAsString(@Nonnull PropertyId<T> propertyId) { + @SuppressWarnings({"unchecked", "rawtypes"}) + PropertyId<T>.Value value = (PropertyId.Value) valuesById.get(propertyId); + + if (value == null) { + throw new ConfigurationError("Property '" + propertyId.getName() + + "' is unknown (see annotation @" + HasKeyId.class.getSimpleName() + + " or requiredIf expression)"); + } + + return value.getString(); + } + + @Override + @Nonnull + public synchronized <T> T get(@Nonnull ObjectId<T> objectId) { + @SuppressWarnings("unchecked") + T instance = (T) instancesById.get(objectId); + + if (instance == null) { + instance = objectId.createObject(); + instancesById.put(objectId, instance); } return instance; } + @SuppressWarnings("unchecked") + @Override + @Nonnull + public <T, S> T get(@Nonnull KeyId<T, S> keyId) { + if (keyId instanceof PropertyId) { + return get((PropertyId<T>) keyId); + } else { + return get((ObjectId<T>) keyId); + } + } + @Override @Nonnull public Collection<PropertyId<?>> getPropertyIds() { ArrayList<PropertyId<?>> result = - new ArrayList<PropertyId<?>>(instanceValuesById.size() + stringValuesById.size()); + new ArrayList<PropertyId<?>>(instancesById.size() + valuesById.size()); - for (KeyId<?, ?> keyId : stringValuesById.keySet()) { + for (KeyId<?, ?> keyId : valuesById.keySet()) { if (keyId.isPublic()) { if (keyId instanceof PropertyId) { result.add((PropertyId<?>) keyId); @@ -98,7 +137,7 @@ class ConfigImpl implements Config { } } - for (KeyId<?, ?> keyId : instanceValuesById.keySet()) { + for (KeyId<?, ?> keyId : instancesById.keySet()) { if (keyId.isPublic()) { if (keyId instanceof PropertyId) { result.add((PropertyId<?>) keyId); @@ -108,25 +147,4 @@ class ConfigImpl implements Config { return result; } - - @Override - @Nonnull - public <T> String getAsString(@Nonnull PropertyId<T> propertyId) { - String result; - - result = stringValuesById.get(propertyId); - if (result == null) { - @SuppressWarnings("unchecked") - T instance = (T) instanceValuesById.get(propertyId); - if (instance == null) { - throw new ConfigurationError("Property '" + propertyId.getName() - + "' is unknown (see annotation @" + HasKeyId.class.getSimpleName() - + " or requiredIf expression)"); - } - - result = propertyId.getCodec().formatValue(instance); - } - - return result; - } } diff --git a/sched/src/com/android/sched/util/config/ConfigurationError.java b/sched/src/com/android/sched/util/config/ConfigurationError.java index 9ca2b2b..fef0795 100644 --- a/sched/src/com/android/sched/util/config/ConfigurationError.java +++ b/sched/src/com/android/sched/util/config/ConfigurationError.java @@ -16,6 +16,7 @@ package com.android.sched.util.config; +import com.android.sched.util.codec.CheckingException; import com.android.sched.util.codec.ParsingException; import javax.annotation.Nonnull; @@ -43,4 +44,8 @@ public class ConfigurationError extends Error { public ConfigurationError(@Nonnull ParsingException e) { super(e.getMessage(), e); } + + public ConfigurationError(@Nonnull CheckingException e) { + super(e.getMessage(), e); + } } diff --git a/sched/src/com/android/sched/util/config/GatherConfigBuilder.java b/sched/src/com/android/sched/util/config/GatherConfigBuilder.java index b923541..133ef88 100644 --- a/sched/src/com/android/sched/util/config/GatherConfigBuilder.java +++ b/sched/src/com/android/sched/util/config/GatherConfigBuilder.java @@ -54,7 +54,21 @@ public class GatherConfigBuilder { } @Nonnull - public GatherConfigBuilder set(@Nonnull String name, @Nonnull String value) { + public GatherConfigBuilder setString(@Nonnull String name, @Nonnull String value) { + try { + builder.setString(name, value); + } catch (PropertyIdException e) { + exceptions.appendException(e); + } catch (UnknownPropertyNameException e) { + exceptions.appendException(e); + } + + return this; + } + + + @Nonnull + public <T> GatherConfigBuilder set(@Nonnull String name, @Nonnull T value) { try { builder.set(name, value); } catch (PropertyIdException e) { @@ -67,9 +81,24 @@ public class GatherConfigBuilder { } @Nonnull - public GatherConfigBuilder set( + public GatherConfigBuilder setString( @Nonnull String name, @Nonnull String value, @Nonnull Location location) { try { + builder.setString(name, value, location); + } catch (UnknownPropertyNameException e) { + exceptions.appendException(e); + } catch (PropertyIdException e) { + exceptions.appendException(e); + } + + return this; + } + + + @Nonnull + public <T> GatherConfigBuilder set( + @Nonnull String name, @Nonnull T value, @Nonnull Location location) { + try { builder.set(name, value, location); } catch (UnknownPropertyNameException e) { exceptions.appendException(e); @@ -81,7 +110,20 @@ public class GatherConfigBuilder { } @Nonnull - public GatherConfigBuilder set(@Nonnull PropertyId<?> propertyId, @Nonnull String value) { + public GatherConfigBuilder setString(@Nonnull PropertyId<?> propertyId, @Nonnull String value) { + try { + builder.setString(propertyId, value); + } catch (UnknownPropertyIdException e) { + exceptions.appendException(e); + } catch (PropertyIdException e) { + exceptions.appendException(e); + } + + return this; + } + + @Nonnull + public <T> GatherConfigBuilder set(@Nonnull PropertyId<T> propertyId, @Nonnull T value) { try { builder.set(propertyId, value); } catch (UnknownPropertyIdException e) { @@ -94,9 +136,23 @@ public class GatherConfigBuilder { } @Nonnull - public GatherConfigBuilder set( + public GatherConfigBuilder setString( @Nonnull PropertyId<?> propertyId, @Nonnull String value, @Nonnull Location location) { try { + builder.setString(propertyId, value, location); + } catch (UnknownPropertyIdException e) { + exceptions.appendException(e); + } catch (PropertyIdException e) { + exceptions.appendException(e); + } + + return this; + } + + @Nonnull + public <T> GatherConfigBuilder set( + @Nonnull PropertyId<T> propertyId, @Nonnull T value, @Nonnull Location location) { + try { builder.set(propertyId, value, location); } catch (UnknownPropertyIdException e) { exceptions.appendException(e); @@ -151,12 +207,18 @@ public class GatherConfigBuilder { */ @Nonnull public Config build() throws ConfigurationException { + Config config; + try { - return builder.build(); + config = builder.build(); } catch (ConfigurationException e) { exceptions.appendException(e); throw exceptions.getException(); } + + exceptions.throwIfNecessary(); + + return config; } @Nonnull @@ -180,6 +242,10 @@ public class GatherConfigBuilder { return this; } + // + // Default location + // + public void pushDefaultLocation(@Nonnull Location location) { builder.pushDefaultLocation(location); } @@ -187,4 +253,128 @@ public class GatherConfigBuilder { public void popDefaultLocation() { builder.popDefaultLocation(); } + + // + // Commodity helper + // + + @Nonnull + public GatherConfigBuilder set(@Nonnull PropertyId<Boolean> propertyId, boolean value) { + set(propertyId, Boolean.valueOf(value)); + + return this; + } + + @Nonnull + public GatherConfigBuilder set(@Nonnull PropertyId<Boolean> propertyId, boolean value, + @Nonnull Location location) { + set(propertyId, Boolean.valueOf(value), location); + + return this; + } + + @Nonnull + public GatherConfigBuilder set(@Nonnull PropertyId<Byte> propertyId, byte value) { + set(propertyId, Byte.valueOf(value)); + + return this; + } + + @Nonnull + public GatherConfigBuilder set(@Nonnull PropertyId<Byte> propertyId, byte value, + @Nonnull Location location) { + set(propertyId, Byte.valueOf(value), location); + + return this; + } + + @Nonnull + public GatherConfigBuilder set(@Nonnull PropertyId<Short> propertyId, short value) { + set(propertyId, Short.valueOf(value)); + + return this; + } + + @Nonnull + public GatherConfigBuilder set(@Nonnull PropertyId<Short> propertyId, short value, + @Nonnull Location location) { + set(propertyId, Short.valueOf(value), location); + + return this; + } + + @Nonnull + public GatherConfigBuilder set(@Nonnull PropertyId<Character> propertyId, char value) { + set(propertyId, Character.valueOf(value)); + + return this; + } + + @Nonnull + public GatherConfigBuilder set(@Nonnull PropertyId<Character> propertyId, char value, + @Nonnull Location location) { + set(propertyId, Character.valueOf(value), location); + + return this; + } + + @Nonnull + public GatherConfigBuilder set(@Nonnull PropertyId<Integer> propertyId, int value) { + set(propertyId, Integer.valueOf(value)); + + return this; + } + + @Nonnull + public GatherConfigBuilder set(@Nonnull PropertyId<Integer> propertyId, int value, + @Nonnull Location location) { + set(propertyId, Integer.valueOf(value), location); + + return this; + } + + @Nonnull + public GatherConfigBuilder set(@Nonnull PropertyId<Long> propertyId, long value) { + set(propertyId, Long.valueOf(value)); + + return this; + } + + @Nonnull + public GatherConfigBuilder set(@Nonnull PropertyId<Long> propertyId, long value, + @Nonnull Location location) { + set(propertyId, Long.valueOf(value), location); + + return this; + } + + @Nonnull + public GatherConfigBuilder set(@Nonnull PropertyId<Float> propertyId, float value) { + set(propertyId, Float.valueOf(value)); + + return this; + } + + @Nonnull + public GatherConfigBuilder set(@Nonnull PropertyId<Float> propertyId, float value, + @Nonnull Location location) { + set(propertyId, Float.valueOf(value), location); + + return this; + } + + @Nonnull + public GatherConfigBuilder set(@Nonnull PropertyId<Double> propertyId, double value) { + set(propertyId, Double.valueOf(value)); + + return this; + } + + @Nonnull + public GatherConfigBuilder set(@Nonnull PropertyId<Double> propertyId, double value, + @Nonnull Location location) { + set(propertyId, Double.valueOf(value), location); + + return this; + } } diff --git a/sched/src/com/android/sched/util/config/InternalConfig.java b/sched/src/com/android/sched/util/config/InternalConfig.java new file mode 100644 index 0000000..0f96a98 --- /dev/null +++ b/sched/src/com/android/sched/util/config/InternalConfig.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sched.util.config; + +import com.android.sched.util.config.id.PropertyId; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; + +/** + * The interface that deals with configuration properties in private 'get' mode. This interface is + * for private use only (config framework itself). + */ +public interface InternalConfig { + @CheckForNull + public <T> T getObjectIfAny(@Nonnull PropertyId<T> propertyId); +} diff --git a/sched/src/com/android/sched/util/config/ThreadConfig.java b/sched/src/com/android/sched/util/config/ThreadConfig.java index df8cfa5..da532fd 100644 --- a/sched/src/com/android/sched/util/config/ThreadConfig.java +++ b/sched/src/com/android/sched/util/config/ThreadConfig.java @@ -16,7 +16,14 @@ package com.android.sched.util.config; -import com.android.sched.util.config.id.KeyId; +import com.android.sched.util.config.id.ObjectId; +import com.android.sched.util.config.id.PropertyId; +import com.android.sched.util.log.Tracer; +import com.android.sched.util.log.TracerFactory; +import com.android.sched.util.log.stats.Counter; +import com.android.sched.util.log.stats.CounterImpl; +import com.android.sched.util.log.stats.StatisticId; +import com.android.sched.util.log.tracer.TracerEventType; import javax.annotation.Nonnull; @@ -25,6 +32,11 @@ import javax.annotation.Nonnull; */ public class ThreadConfig { @Nonnull + public static final StatisticId<Counter> TLS_READ = new StatisticId<Counter>( + "sched.config.tls.read", "Reading TLS to get current config", + CounterImpl.class, Counter.class); + + @Nonnull private static final Config unitializedConfig = new UninitializedConfig(); @Nonnull @@ -37,8 +49,27 @@ public class ThreadConfig { }; @Nonnull - public static <T, S> T get(@Nonnull KeyId<T, S> keyId) { - return threadLocalConfig.get().get(keyId); + public static <T> T get(@Nonnull PropertyId<T> propertyId) { + Config config = threadLocalConfig.get(); + updateStatistic(config); + + return config.get(propertyId); + } + + @Nonnull + public static <T> T get(@Nonnull ObjectId<T> objectId) { + Config config = threadLocalConfig.get(); + updateStatistic(config); + + return config.get(objectId); + } + + private static void updateStatistic(@Nonnull Config config) { + Tracer tracer = ((InternalConfig) config).<Tracer> getObjectIfAny(TracerFactory.TRACER); + + if (tracer != null && tracer.getCurrentEventType() != TracerEventType.NOEVENT) { + tracer.getStatistic(TLS_READ).incValue(); + } } @Nonnull diff --git a/sched/src/com/android/sched/util/config/UninitializedConfig.java b/sched/src/com/android/sched/util/config/UninitializedConfig.java index f87e628..b6c8e7f 100644 --- a/sched/src/com/android/sched/util/config/UninitializedConfig.java +++ b/sched/src/com/android/sched/util/config/UninitializedConfig.java @@ -17,16 +17,29 @@ package com.android.sched.util.config; import com.android.sched.util.config.id.KeyId; +import com.android.sched.util.config.id.ObjectId; import com.android.sched.util.config.id.PropertyId; import java.util.Collection; +import javax.annotation.CheckForNull; import javax.annotation.Nonnull; /** * This object represents a {@link Config} which has not been created by the builder. */ -class UninitializedConfig implements Config { +class UninitializedConfig implements Config, InternalConfig { + @Override + @Nonnull + public <T> T get(@Nonnull PropertyId<T> propertyId) { + throw new ConfigurationError("Configuration has not been initialized"); + } + + @Override + @Nonnull + public <T> T get(@Nonnull ObjectId<T> objectId) { + throw new ConfigurationError("Configuration has not been initialized"); + } @Override @Nonnull @@ -45,4 +58,10 @@ class UninitializedConfig implements Config { public Collection<PropertyId<?>> getPropertyIds() { throw new ConfigurationError("Configuration has not been initialized"); } + + @Override + @CheckForNull + public <T> T getObjectIfAny(@Nonnull PropertyId<T> propertyId) { + throw new ConfigurationError("Configuration has not been initialized"); + } } diff --git a/sched/src/com/android/sched/util/config/VariableDoesNotMatchConfigurationException.java b/sched/src/com/android/sched/util/config/VariableDoesNotMatchConfigurationException.java index 06c711c..788e852 100644 --- a/sched/src/com/android/sched/util/config/VariableDoesNotMatchConfigurationException.java +++ b/sched/src/com/android/sched/util/config/VariableDoesNotMatchConfigurationException.java @@ -25,7 +25,7 @@ public class VariableDoesNotMatchConfigurationException extends VariableConfigur private static final long serialVersionUID = 1L; public VariableDoesNotMatchConfigurationException(@Nonnull String variable) { - super(variable, "Environment variable '" + variable + " does not match any properties"); + super(variable, "Environment variable '" + variable + "' does not match any properties"); } public VariableDoesNotMatchConfigurationException( diff --git a/sched/src/com/android/sched/util/config/VariableMatchesSeveralConfigurationException.java b/sched/src/com/android/sched/util/config/VariableMatchesSeveralConfigurationException.java index f6eaab6..1e00409 100644 --- a/sched/src/com/android/sched/util/config/VariableMatchesSeveralConfigurationException.java +++ b/sched/src/com/android/sched/util/config/VariableMatchesSeveralConfigurationException.java @@ -31,7 +31,7 @@ public class VariableMatchesSeveralConfigurationException extends VariableConfig public VariableMatchesSeveralConfigurationException( @Nonnull String variable, @Nonnull PropertyId<?> propertyId) { - super(variable, "Environment variable '" + variable + " matches several properties: '" + super(variable, "Environment variable '" + variable + "' matches several properties: '" + propertyId.getName() + "'"); this.propertyId = propertyId; } diff --git a/sched/src/com/android/sched/util/config/ZipLocation.java b/sched/src/com/android/sched/util/config/ZipLocation.java index 3a6d9be..af78749 100644 --- a/sched/src/com/android/sched/util/config/ZipLocation.java +++ b/sched/src/com/android/sched/util/config/ZipLocation.java @@ -30,11 +30,11 @@ public class ZipLocation extends Location { private final Location archive; @Nonnull - private final String pathInArchive; + private final String entryName; public ZipLocation(@Nonnull Location archive, @Nonnull ZipEntry entry) { this.archive = archive; - this.pathInArchive = '/' + entry.getName(); + this.entryName = entry.getName(); } @Override @@ -46,7 +46,7 @@ public class ZipLocation extends Location { sb.append(archive.getDescription()).append(", "); } - return sb.append("entry '").append(pathInArchive).append('\'').toString(); + return sb.append("entry '/").append(entryName).append('\'').toString(); } @Nonnull @@ -54,22 +54,20 @@ public class ZipLocation extends Location { return archive; } - /** - * @return the pathInArchive - */ - public String getPathInArchive() { - return pathInArchive; + @Nonnull + public String getEntryName() { + return entryName; } @Override public final boolean equals(Object obj) { return obj instanceof ZipLocation && ((ZipLocation) obj).archive.equals(archive) - && ((ZipLocation) obj).getPathInArchive().equals(pathInArchive); + && ((ZipLocation) obj).entryName.equals(entryName); } @Override public final int hashCode() { - return archive.hashCode() ^ pathInArchive.hashCode(); + return archive.hashCode() ^ entryName.hashCode(); } } diff --git a/sched/src/com/android/sched/util/config/id/BooleanPropertyId.java b/sched/src/com/android/sched/util/config/id/BooleanPropertyId.java index ebe2399..a97bd45 100644 --- a/sched/src/com/android/sched/util/config/id/BooleanPropertyId.java +++ b/sched/src/com/android/sched/util/config/id/BooleanPropertyId.java @@ -52,6 +52,21 @@ public class BooleanPropertyId extends PropertyId<Boolean> { @Override @Nonnull + public BooleanPropertyId addDefaultValue (@Nonnull Boolean defaultValue) { + super.addDefaultValue(defaultValue); + + return this; + } + + @Nonnull + public BooleanPropertyId addDefaultValue (boolean defaultValue) { + super.addDefaultValue(Boolean.valueOf(defaultValue)); + + return this; + } + + @Override + @Nonnull public BooleanPropertyId requiredIf(@Nonnull BooleanExpression expression) { super.requiredIf(expression); diff --git a/sched/src/com/android/sched/util/config/id/DoublePropertyId.java b/sched/src/com/android/sched/util/config/id/DoublePropertyId.java index 786c9b2..111fd5e 100644 --- a/sched/src/com/android/sched/util/config/id/DoublePropertyId.java +++ b/sched/src/com/android/sched/util/config/id/DoublePropertyId.java @@ -51,6 +51,21 @@ public class DoublePropertyId extends PropertyId<Double> { @Override @Nonnull + public DoublePropertyId addDefaultValue (@Nonnull Double defaultValue) { + super.addDefaultValue(defaultValue); + + return this; + } + + @Nonnull + public DoublePropertyId addDefaultValue (@Nonnull double defaultValue) { + super.addDefaultValue(Double.valueOf(defaultValue)); + + return this; + } + + @Override + @Nonnull public DoublePropertyId requiredIf(@Nonnull BooleanExpression expression) { super.requiredIf(expression); diff --git a/sched/src/com/android/sched/util/config/id/EnumPropertyId.java b/sched/src/com/android/sched/util/config/id/EnumPropertyId.java index 493f1e5..c209e3c 100644 --- a/sched/src/com/android/sched/util/config/id/EnumPropertyId.java +++ b/sched/src/com/android/sched/util/config/id/EnumPropertyId.java @@ -51,6 +51,14 @@ public class EnumPropertyId<T extends Enum<T>> extends PropertyId<T> { @Override @Nonnull + public EnumPropertyId<T> addDefaultValue (@Nonnull T defaultValue) { + super.addDefaultValue(defaultValue); + + return this; + } + + @Override + @Nonnull public EnumPropertyId<T> requiredIf(@Nonnull BooleanExpression expression) { super.requiredIf(expression); diff --git a/sched/src/com/android/sched/util/config/id/IntegerPropertyId.java b/sched/src/com/android/sched/util/config/id/IntegerPropertyId.java index 4b584cd..ce03c10 100644 --- a/sched/src/com/android/sched/util/config/id/IntegerPropertyId.java +++ b/sched/src/com/android/sched/util/config/id/IntegerPropertyId.java @@ -44,6 +44,20 @@ public class IntegerPropertyId extends LongPropertyId { return this; } + @Nonnull + public IntegerPropertyId addDefaultValue (@Nonnull Integer defaultValue) { + super.addDefaultValue(Long.valueOf(defaultValue.longValue())); + + return this; + } + + @Nonnull + public IntegerPropertyId addDefaultValue (@Nonnull int defaultValue) { + super.addDefaultValue(Long.valueOf(defaultValue)); + + return this; + } + @Override @Nonnull public IntegerPropertyId requiredIf(@Nonnull BooleanExpression expression) { diff --git a/sched/src/com/android/sched/util/config/id/KeyId.java b/sched/src/com/android/sched/util/config/id/KeyId.java index 40b157c..3576fab 100644 --- a/sched/src/com/android/sched/util/config/id/KeyId.java +++ b/sched/src/com/android/sched/util/config/id/KeyId.java @@ -21,9 +21,6 @@ import com.android.sched.util.config.PropertyIdException; import com.android.sched.util.config.expression.BooleanExpression; import com.android.sched.util.config.expression.PropertyNotRequiredException; -import java.util.ArrayList; -import java.util.List; - import javax.annotation.CheckForNull; import javax.annotation.Nonnull; @@ -38,12 +35,6 @@ public abstract class KeyId<T, S> { @Nonnull private final String name; - @CheckForNull - private BooleanExpression requiredIf; - - @Nonnull - private final List<S> defaultValues = new ArrayList<S>(2); - public KeyId(@Nonnull String name) { this.name = name; } @@ -55,17 +46,8 @@ public abstract class KeyId<T, S> { public abstract boolean isPublic(); - @Nonnull - public KeyId<T, S> addDefaultValue(@Nonnull S defaultValue) { - defaultValues.add(defaultValue); - - return this; - } - - @Nonnull - public List<S> getDefaultValues() { - return defaultValues; - } + @CheckForNull + private BooleanExpression requiredIf; @Nonnull public KeyId<T, S> requiredIf(@Nonnull BooleanExpression expression) { diff --git a/sched/src/com/android/sched/util/config/id/ListPropertyId.java b/sched/src/com/android/sched/util/config/id/ListPropertyId.java index 7f7c999..075fa00 100644 --- a/sched/src/com/android/sched/util/config/id/ListPropertyId.java +++ b/sched/src/com/android/sched/util/config/id/ListPropertyId.java @@ -55,6 +55,15 @@ public class ListPropertyId<T> extends PropertyId<List<T>> { return this; } + + @Override + @Nonnull + public ListPropertyId<T> addDefaultValue (@Nonnull List<T> defaultValue) { + super.addDefaultValue(defaultValue); + + return this; + } + @Override @Nonnull public ListPropertyId<T> requiredIf(@Nonnull BooleanExpression expression) { diff --git a/sched/src/com/android/sched/util/config/id/LongPropertyId.java b/sched/src/com/android/sched/util/config/id/LongPropertyId.java index cb1e377..7f95102 100644 --- a/sched/src/com/android/sched/util/config/id/LongPropertyId.java +++ b/sched/src/com/android/sched/util/config/id/LongPropertyId.java @@ -51,6 +51,21 @@ public class LongPropertyId extends PropertyId<Long> { @Override @Nonnull + public LongPropertyId addDefaultValue (@Nonnull Long defaultValue) { + super.addDefaultValue(defaultValue); + + return this; + } + + @Nonnull + public LongPropertyId addDefaultValue (@Nonnull long defaultValue) { + super.addDefaultValue(Long.valueOf(defaultValue)); + + return this; + } + + @Override + @Nonnull public LongPropertyId requiredIf(@Nonnull BooleanExpression expression) { super.requiredIf(expression); diff --git a/sched/src/com/android/sched/util/config/id/ProbabilityPropertyId.java b/sched/src/com/android/sched/util/config/id/ProbabilityPropertyId.java index 1e173e1..f83165b 100644 --- a/sched/src/com/android/sched/util/config/id/ProbabilityPropertyId.java +++ b/sched/src/com/android/sched/util/config/id/ProbabilityPropertyId.java @@ -45,6 +45,22 @@ public class ProbabilityPropertyId extends DoublePropertyId { @Override @Nonnull + public ProbabilityPropertyId addDefaultValue (@Nonnull Double defaultValue) { + super.addDefaultValue(defaultValue); + + return this; + } + + @Override + @Nonnull + public ProbabilityPropertyId addDefaultValue (@Nonnull double defaultValue) { + super.addDefaultValue(defaultValue); + + return this; + } + + @Override + @Nonnull public ProbabilityPropertyId requiredIf(@Nonnull BooleanExpression expression) { super.requiredIf(expression); @@ -70,7 +86,7 @@ public class ProbabilityPropertyId extends DoublePropertyId { } private boolean checkRange(double value) { - return value >= 0 && value <= 1; + return value >= 0.0 && value <= 1.0; } /** diff --git a/sched/src/com/android/sched/util/config/id/PropertyId.java b/sched/src/com/android/sched/util/config/id/PropertyId.java index 7e9fa28..280bf08 100644 --- a/sched/src/com/android/sched/util/config/id/PropertyId.java +++ b/sched/src/com/android/sched/util/config/id/PropertyId.java @@ -23,6 +23,9 @@ import com.android.sched.util.codec.StringCodec; import com.android.sched.util.config.ConfigurationError; import com.android.sched.util.config.expression.BooleanExpression; +import java.util.ArrayList; +import java.util.List; + import javax.annotation.CheckForNull; import javax.annotation.Nonnull; @@ -31,48 +34,55 @@ import javax.annotation.Nonnull; * @param <T> Type of the configuration property. */ public class PropertyId<T> extends KeyId<T, String> { - @Nonnull private final String description; @Nonnull - private final StringCodec<T> parser; + private final StringCodec<T> codec; + @Nonnull + private final List<Value> defaultValues = new ArrayList<Value>(1); @CheckForNull - private String defaultValue = null; - private boolean defaultValueAvailable = false; + private Value defaultValue = null; + private boolean defaultValueAvailable = false; private boolean isPublic = true; public static <T> PropertyId<T> create( - @Nonnull String name, @Nonnull String description, @Nonnull StringCodec<T> parser) { - return new PropertyId<T>(name, description, parser); + @Nonnull String name, @Nonnull String description, @Nonnull StringCodec<T> codec) { + return new PropertyId<T>(name, description, codec); } - protected PropertyId( - @Nonnull String name, @Nonnull String description, @Nonnull StringCodec<T> parser) { + protected PropertyId(@Nonnull String name, @Nonnull String description, + @Nonnull StringCodec<T> codec) { super(name); this.description = description; - this.parser = parser; + this.codec = codec; } - @Override @Nonnull public PropertyId<T> addDefaultValue(@Nonnull String defaultValue) { - super.addDefaultValue(defaultValue); + defaultValues.add(new Value(defaultValue)); + + return this; + } + + @Nonnull + public PropertyId<T> addDefaultValue(@Nonnull T defaultValue) { + defaultValues.add(new Value(defaultValue)); return this; } @CheckForNull - public String getDefaultValue(@Nonnull CodecContext context) { + public Value getDefaultValue(@Nonnull CodecContext context) { if (!defaultValueAvailable) { ParsingException lastException = null; - for (String value : getDefaultValues()) { + for (Value value : getDefaultValues()) { try { - parser.checkString(context, value); + value.check(context); defaultValue = value; break; } catch (ParsingException e) { @@ -91,6 +101,11 @@ public class PropertyId<T> extends KeyId<T, String> { } @Nonnull + public List<Value> getDefaultValues() { + return defaultValues; + } + + @Nonnull public String getDescription() { return description; } @@ -100,6 +115,7 @@ public class PropertyId<T> extends KeyId<T, String> { return isPublic; } + @Nonnull public PropertyId<T> makePrivate() { isPublic = false; return this; @@ -107,7 +123,7 @@ public class PropertyId<T> extends KeyId<T, String> { @Nonnull public StringCodec<T> getCodec() { - return parser; + return codec; } @Override @@ -118,4 +134,148 @@ public class PropertyId<T> extends KeyId<T, String> { return this; } + public class Value { + @Nonnull + private IValue<T> value; + + public Value (@Nonnull T value) { + this.value = new IValueObject<T>(value); + } + + public Value (@Nonnull String value) { + this.value = new IValueString(value); + } + + public synchronized void check(@Nonnull CodecContext context) throws ParsingException { + value = value.check(context); + } + + @Nonnull + public String getString() { + return value.getString(); + } + + @Nonnull + public synchronized T getObject(@Nonnull CodecContext context) { + value = value.getValueObject(context); + + return ((IValueObject<T>) value).getObject(); + } + + + @CheckForNull + public synchronized T getObjectIfAny() { + if (value instanceof IValueObject) { + return ((IValueObject<T>) value).getObject(); + } else { + return null; + } + } + } + + // + // Private IValue hierarchy + // + + private static interface IValue<T> { + @Nonnull + public IValue<T> check(@Nonnull CodecContext context) throws ParsingException; + + @Nonnull + public PropertyId<?>.IValueObject<T> getValueObject(@Nonnull CodecContext context); + + @Nonnull + public String getString(); + } + + private class IValueString implements IValue<T> { + @Nonnull + private final String value; + + public IValueString (@Nonnull String value) { + this.value = value; + } + + @Override + public String getString() { + return value; + } + + @Override + @Nonnull + public IValue<T> check(@Nonnull CodecContext context) throws ParsingException { + T val = PropertyId.this.codec.checkString(context, value); + + if (val != null) { + return new IValueObject<T>(val); + } else { + return new IValueCheckedString(value); + } + } + + @Override + @Nonnull + public PropertyId<T>.IValueObject<T> getValueObject(@Nonnull CodecContext context) { + throw new AssertionError(); + } + } + + private class IValueCheckedString implements IValue<T> { + @Nonnull + private final String value; + + private IValueCheckedString (@Nonnull String value) { + this.value = value; + } + + @Override + public String getString() { + return value; + } + + @Override + @Nonnull + public IValue<T> check(@Nonnull CodecContext context) { + return this; + } + + @Override + @Nonnull + public PropertyId<?>.IValueObject<T> getValueObject(@Nonnull CodecContext context) { + return new IValueObject<T>(PropertyId.this.codec.parseString(context, value)); + } + } + + private class IValueObject<T> implements IValue<T> { + @Nonnull + private final T value; + + public IValueObject (@Nonnull T value) { + this.value = value; + } + + @SuppressWarnings("unchecked") + @Override + @Nonnull + public String getString() { + return ((PropertyId<T>) (PropertyId.this)).codec.formatValue(value); + } + + @Override + @Nonnull + public IValue<T> check(@Nonnull CodecContext context) { + return this; + } + + @Override + @Nonnull + public PropertyId<?>.IValueObject<T> getValueObject(@Nonnull CodecContext context) { + return this; + } + + @Nonnull + public T getObject() { + return value; + } + } } diff --git a/sched/src/com/android/sched/util/log/EventType.java b/sched/src/com/android/sched/util/log/EventType.java index 040d99a..418ccc3 100644 --- a/sched/src/com/android/sched/util/log/EventType.java +++ b/sched/src/com/android/sched/util/log/EventType.java @@ -20,6 +20,5 @@ package com.android.sched.util.log; * Represents a event type of an event. */ public interface EventType { - public String getColor(); public String getName(); }
\ No newline at end of file diff --git a/sched/src/com/android/sched/util/log/SchedEventType.java b/sched/src/com/android/sched/util/log/SchedEventType.java index dd2352b..0344d6e 100644 --- a/sched/src/com/android/sched/util/log/SchedEventType.java +++ b/sched/src/com/android/sched/util/log/SchedEventType.java @@ -23,24 +23,15 @@ import javax.annotation.Nonnull; * Represents a type of event whose performance is tracked */ public enum SchedEventType implements EventType { - REFLECTIONS("Reflections", "Yellow"), // - INSTANCIER("Schedulable instancier", "Blue"), // - PLANBUILDER("Plan builder", "Red"); + REFLECTIONS("Reflections"), + INSTANCIER("Schedulable instancier"), + PLANBUILDER("Plan builder"); @Nonnull - private final String cssColor; - @Nonnull private final String name; - SchedEventType(@Nonnull String name, @Nonnull String cssColor) { + SchedEventType(@Nonnull String name) { this.name = name; - this.cssColor = cssColor; - } - - @Override - @Nonnull - public String getColor() { - return cssColor; } @Override diff --git a/sched/src/com/android/sched/util/log/StatisticOnlyTracer.java b/sched/src/com/android/sched/util/log/StatisticOnlyTracer.java index da5323b..81095e5 100644 --- a/sched/src/com/android/sched/util/log/StatisticOnlyTracer.java +++ b/sched/src/com/android/sched/util/log/StatisticOnlyTracer.java @@ -36,11 +36,13 @@ import com.android.sched.util.table.ReportPrinterFactory; import com.android.sched.util.table.SimpleTable; import com.android.sched.util.table.Table; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -48,6 +50,7 @@ import java.util.Set; import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.logging.Level; import java.util.logging.Logger; @@ -147,7 +150,7 @@ public final class StatisticOnlyTracer implements Tracer { @Override @Nonnull public String toString() { - return "Dummy"; + return "Singleton"; } @Override @@ -221,7 +224,7 @@ public final class StatisticOnlyTracer implements Tracer { @Override @Nonnull public EventType getCurrentEventType() { - return TracerEventType.NOEVENT; + return TracerEventType.SINGLETON; } @Override @@ -246,77 +249,116 @@ public final class StatisticOnlyTracer implements Tracer { objects = new HashMap< Class<? extends ObjectWatcher<?>>, WeakHashMap<Object, ObjectWatcher<Object>>>(); + // Map a class C to a list of watcher classes that watch instances of type C @Nonnull - private final Map<Class<?>, Class<? extends ObjectWatcher<?>>> watchers = - new HashMap<Class<?>, Class<? extends ObjectWatcher<?>>>(); + private final Map<Class<?>, List<Class<? extends ObjectWatcher<?>>>> watchers = + new HashMap<Class<?>, List<Class<? extends ObjectWatcher<?>>>>(); + // Set of classes not watched (speedup non watched classes) @Nonnull private final Set<Class<?>> notWatched = new HashSet<Class<?>>(); @Nonnull - private final Object watcherLock = new Object(); + private final ReentrantReadWriteLock watcherLock = new ReentrantReadWriteLock(); @Override - public synchronized <T> void registerWatcher(@Nonnull Class<T> objectClass, + public synchronized <T> void registerWatcher(@Nonnull Class<T> rootWatchedClass, @Nonnull Class<? extends ObjectWatcher<? extends T>> watcherClass) { WeakHashMap<Object, ObjectWatcher<Object>> map = new WeakHashMap<Object, ObjectWatcher<Object>>(); - synchronized (watcherLock) { + watcherLock.writeLock().lock(); + try { objects.put(watcherClass, map); - watchers.put(objectClass, watcherClass); - for (Class<?> cls : notWatched) { - if (objectClass.isAssignableFrom(cls)) { + List<Class<? extends ObjectWatcher<?>>> list = watchers.get(rootWatchedClass); + if (list == null) { + list = new ArrayList<Class<? extends ObjectWatcher<?>>>(1); + watchers.put(rootWatchedClass, list); + } + + list.add(watcherClass); + + Iterator<Class<?>> iterNotWatched = notWatched.iterator(); + while (iterNotWatched.hasNext()) { + Class<?> watchedClass = iterNotWatched.next(); + if (rootWatchedClass.isAssignableFrom(watchedClass)) { logger.log(Level.INFO, "Watcher ''{0}'' missed some instances of type ''{1}''", - new Object[] {watcherClass.getName(), cls.getName()}); + new Object[] {watcherClass.getName(), watchedClass.getName()}); - watchers.put(cls, watcherClass); - notWatched.remove(objectClass); + list = watchers.get(watchedClass); + if (list == null) { + list = new ArrayList<Class<? extends ObjectWatcher<?>>>(1); + watchers.put(watchedClass, list); + } + + list.add(watcherClass); + iterNotWatched.remove(); } } + } finally { + watcherLock.writeLock().unlock(); } } @Override public void registerObject(@Nonnull Object object, @Nonnegative long size, int count) { - Class<? extends ObjectWatcher<?>> watcherClass = null; + enable.set(Boolean.FALSE); + Class<?> objectClass = object.getClass(); + List<Class<? extends ObjectWatcher<?>>> list = null; - synchronized (watcherLock) { - if (notWatched.contains(object.getClass())) { + watcherLock.readLock().lock(); + try { + // If this object is not watched explicitly, go away + if (notWatched.contains(objectClass)) { return; } - watcherClass = watchers.get(object.getClass()); - if (watcherClass == null) { - for (Entry<Class<?>, Class<? extends ObjectWatcher<?>>> entry : watchers.entrySet()) { - if (entry.getKey().isAssignableFrom(object.getClass())) { - watcherClass = entry.getValue(); - break; + list = watchers.get(objectClass); + } finally { + watcherLock.readLock().unlock(); + } + + if (list == null) { + watcherLock.writeLock().lock(); + try { + list = watchers.get(objectClass); + if (list == null) { + list = new ArrayList<Class<? extends ObjectWatcher<?>>>(1); + + for (Entry<Class<?>, List<Class<? extends ObjectWatcher<?>>>> entry : + watchers.entrySet()) { + if (entry.getKey().isAssignableFrom(objectClass)) { + list.addAll(entry.getValue()); + } } } - if (watcherClass != null) { - watchers.put(object.getClass(), watcherClass); + if (!list.isEmpty()) { + watchers.put(objectClass, list); } else { - notWatched.add(object.getClass()); - return; + notWatched.add(objectClass); } + } finally { + watcherLock.writeLock().unlock(); } } - try { - @SuppressWarnings("unchecked") - ObjectWatcher<Object> watcher = (ObjectWatcher<Object>) watcherClass.newInstance(); - WeakHashMap<Object, ObjectWatcher<Object>> weak = objects.get(watcherClass); - assert weak != null; // If watchers contains object.getClass, then objects contains it also, - // see registerWatcher - if (watcher.notifyInstantiation(object, size, count, getCurrentEventType())) { - weak.put(object, watcher); + for (Class<? extends ObjectWatcher<?>> watcherClass : list) { + try { + @SuppressWarnings("unchecked") + ObjectWatcher<Object> watcher = (ObjectWatcher<Object>) watcherClass.newInstance(); + + if (watcher.notifyInstantiation(object, size, count, getCurrentEventType())) { + WeakHashMap<Object, ObjectWatcher<Object>> weak = objects.get(watcherClass); + assert weak != null; // If watchers contains object.getClass, then objects contains it + // also, see registerWatcher + weak.put(object, watcher); + } + } catch (InstantiationException e) { + logger.log(Level.WARNING, "Can not instantiate Watcher", e); + } catch (IllegalAccessException e) { + logger.log(Level.WARNING, "Can not instantiate Watcher", e); } - } catch (InstantiationException e) { - logger.log(Level.WARNING, "Can not instantiate Watcher", e); - } catch (IllegalAccessException e) { - logger.log(Level.WARNING, "Can not instantiate Watcher", e); } } } diff --git a/sched/src/com/android/sched/util/log/stats/Alloc.java b/sched/src/com/android/sched/util/log/stats/Alloc.java index 2bf2989..ae1b50b 100644 --- a/sched/src/com/android/sched/util/log/stats/Alloc.java +++ b/sched/src/com/android/sched/util/log/stats/Alloc.java @@ -46,53 +46,10 @@ public class Alloc extends Statistic { @Override @Nonnull - @Deprecated - public Object getValue(@Nonnegative int columnIdx) { - throw new AssertionError(); - } - - @Override - @Nonnull - @Deprecated - public String getHumanReadableValue(@Nonnegative int columnIdx) { - throw new AssertionError(); - } - - @Override - @Nonnull - @Deprecated - public String getDescription(@Nonnegative int columnIdx) { - switch (columnIdx) { - case 0: - return "Count"; - case 1: - return "Size"; - default: - throw new AssertionError(); - } - } - - @Override - @Nonnull - @Deprecated - public String getType(@Nonnegative int columnIdx) { - switch (columnIdx) { - case 0: - return "number"; - case 1: - return "number"; - default: - throw new AssertionError(); - } - } - - @Override - @Nonnull public String getDescription() { return "Allocation"; } - @Nonnull private static final String[] HEADER = new String[] { "Count", diff --git a/sched/src/com/android/sched/util/log/stats/AllocImpl.java b/sched/src/com/android/sched/util/log/stats/AllocImpl.java index bbed19c..df2b01a 100644 --- a/sched/src/com/android/sched/util/log/stats/AllocImpl.java +++ b/sched/src/com/android/sched/util/log/stats/AllocImpl.java @@ -18,7 +18,6 @@ package com.android.sched.util.log.stats; import com.google.common.collect.Iterators; -import com.android.sched.util.log.tracer.probe.MemoryBytesProbe; import com.android.sched.util.table.DataHeader; import com.android.sched.util.table.DataRow; @@ -63,34 +62,6 @@ public class AllocImpl extends Alloc implements DataRow, DataHeader { @Override @Nonnull - @Deprecated - public synchronized Object getValue(@Nonnegative int columnIdx) { - switch (columnIdx) { - case 0: - return Long.valueOf(number); - case 1: - return Long.valueOf(size); - default: - throw new AssertionError(); - } - } - - @Override - @Nonnull - @Deprecated - public synchronized String getHumanReadableValue(@Nonnegative int columnIdx) { - switch (columnIdx) { - case 0: - return Long.toString(number); - case 1: - return MemoryBytesProbe.formatBytes(size); - default: - throw new AssertionError(); - } - } - - @Override - @Nonnull public synchronized Iterator<Object> iterator() { return Iterators.<Object> forArray( Long.valueOf(number), diff --git a/sched/src/com/android/sched/util/log/stats/ArrayAlloc.java b/sched/src/com/android/sched/util/log/stats/ArrayAlloc.java index 5b29bbf..d086b49 100644 --- a/sched/src/com/android/sched/util/log/stats/ArrayAlloc.java +++ b/sched/src/com/android/sched/util/log/stats/ArrayAlloc.java @@ -18,9 +18,9 @@ package com.android.sched.util.log.stats; import com.google.common.collect.ObjectArrays; +import com.android.sched.util.codec.ByteFormatter; import com.android.sched.util.codec.Formatter; import com.android.sched.util.codec.LongCodec; -import com.android.sched.util.codec.ByteFormatter; import javax.annotation.Nonnegative; import javax.annotation.Nonnull; @@ -49,64 +49,6 @@ public class ArrayAlloc extends Statistic { @Override @Nonnull - @Deprecated - public Object getValue(@Nonnegative int columnIdx) { - throw new AssertionError(); - } - - @Override - @Nonnull - @Deprecated - public String getHumanReadableValue(@Nonnegative int columnIdx) { - throw new AssertionError(); - } - - @Override - @Nonnull - @Deprecated - public String getDescription(@Nonnegative int columnIdx) { - switch (columnIdx) { - case 0: - return "Array count"; - case 1: - return "Total size"; - case 2: - return "Total elements"; - case 3: - return "Min elements"; - case 4: - return "Average elements"; - case 5: - return "max elements"; - default: - throw new AssertionError(); - } - } - - @Override - @Nonnull - @Deprecated - public String getType(@Nonnegative int columnIdx) { - switch (columnIdx) { - case 0: - return "number"; - case 1: - return "number"; - case 2: - return "number"; - case 3: - return "number"; - case 4: - return "number"; - case 5: - return "number"; - default: - throw new AssertionError(); - } - } - - @Override - @Nonnull public String getDescription() { return "Array allocation"; } diff --git a/sched/src/com/android/sched/util/log/stats/ArrayAllocImpl.java b/sched/src/com/android/sched/util/log/stats/ArrayAllocImpl.java index ff59b55..f5b5c5e 100644 --- a/sched/src/com/android/sched/util/log/stats/ArrayAllocImpl.java +++ b/sched/src/com/android/sched/util/log/stats/ArrayAllocImpl.java @@ -18,7 +18,6 @@ package com.android.sched.util.log.stats; import com.google.common.collect.Iterators; -import com.android.sched.util.log.tracer.probe.MemoryBytesProbe; import com.android.sched.util.table.DataRow; import java.util.Iterator; @@ -69,34 +68,6 @@ public class ArrayAllocImpl extends ArrayAlloc implements DataRow { @Override @Nonnull - @Deprecated - public synchronized Object getValue(@Nonnegative int columnIdx) { - switch (columnIdx) { - case 0: - return Long.valueOf(number); - case 1: - return Long.valueOf(size); - default: - return element.getValue(columnIdx - 1); - } - } - - @Override - @Nonnull - @Deprecated - public synchronized String getHumanReadableValue(@Nonnegative int columnIdx) { - switch (columnIdx) { - case 0: - return Long.toString(number); - case 1: - return MemoryBytesProbe.formatBytes(size); - default: - return element.getHumanReadableValue(columnIdx - 1); - } - } - - @Override - @Nonnull public synchronized Iterator<Object> iterator() { return Iterators.concat( Iterators.forArray(Long.valueOf(number), Long.valueOf(size)), element.iterator()); diff --git a/sched/src/com/android/sched/util/log/stats/Counter.java b/sched/src/com/android/sched/util/log/stats/Counter.java index 8001004..fb6b630 100644 --- a/sched/src/com/android/sched/util/log/stats/Counter.java +++ b/sched/src/com/android/sched/util/log/stats/Counter.java @@ -24,7 +24,7 @@ import javax.annotation.Nonnull; /** - * Represents a counter statistic when statistic is not enabled.. + * Represents a counter statistic when statistic is not enabled. */ public class Counter extends Statistic { protected Counter(@Nonnull StatisticId<? extends Statistic> id) { @@ -60,45 +60,6 @@ public class Counter extends Statistic { @Override @Nonnull - @Deprecated - public Object getValue(@Nonnegative int columnIdx) { - throw new AssertionError(); - } - - @Override - @Nonnull - @Deprecated - public String getHumanReadableValue(@Nonnegative int columnIdx) { - throw new AssertionError(); - } - - @Override - @Nonnull - @Deprecated - public String getDescription(@Nonnegative int columnIdx) { - switch (columnIdx) { - case 0: - return "Number"; - default: - throw new AssertionError(); - } - } - - - @Override - @Nonnull - @Deprecated - public String getType(@Nonnegative int columnIdx) { - switch (columnIdx) { - case 0: - return "number"; - default: - throw new AssertionError(); - } - } - - @Override - @Nonnull public String getDescription() { return "Counter"; } diff --git a/sched/src/com/android/sched/util/log/stats/CounterImpl.java b/sched/src/com/android/sched/util/log/stats/CounterImpl.java index 32aca6c..3aeca0a 100644 --- a/sched/src/com/android/sched/util/log/stats/CounterImpl.java +++ b/sched/src/com/android/sched/util/log/stats/CounterImpl.java @@ -22,7 +22,6 @@ import com.android.sched.util.table.DataRow; import java.util.Iterator; -import javax.annotation.Nonnegative; import javax.annotation.Nonnull; @@ -82,24 +81,6 @@ public class CounterImpl extends Counter implements DataRow { @Override @Nonnull - @Deprecated - public Object getValue(@Nonnegative int columnIdx) { - assert columnIdx == 0; - - return Long.valueOf(value); - } - - @Override - @Nonnull - @Deprecated - public String getHumanReadableValue(@Nonnegative int columnIdx) { - assert columnIdx == 0; - - return Long.valueOf(value).toString(); - } - - @Override - @Nonnull public synchronized Iterator<Object> iterator() { return Iterators.<Object> forArray(Long.valueOf(value)); } diff --git a/sched/src/com/android/sched/util/log/stats/ExtendedSample.java b/sched/src/com/android/sched/util/log/stats/ExtendedSample.java new file mode 100644 index 0000000..5ade77f --- /dev/null +++ b/sched/src/com/android/sched/util/log/stats/ExtendedSample.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sched.util.log.stats; + +import com.android.sched.util.codec.DoubleCodec; +import com.android.sched.util.codec.Formatter; +import com.android.sched.util.codec.LongCodec; + +import javax.annotation.Nonnegative; +import javax.annotation.Nonnull; + +/** + * Extended statistic computation on a set of values when statistic is not enabled. Have Median, + * First quartile, Third quartile. + */ +public class ExtendedSample extends Statistic { + protected ExtendedSample(@Nonnull StatisticId<? extends Statistic> id) { + super(id); + } + + public void add(double value) { + } + + @Nonnegative + public int getCount() { + return 0; + } + + public double getTotal() { + return 0; + } + + public double getMin() { + return Double.NaN; + } + + public double getAverage() { + return Double.NaN; + } + + public double getMax() { + return Double.NaN; + } + + public double getFirstQuartile() { + return Double.NaN; + } + + public double getMedian() { + return Double.NaN; + } + + public double getThirdQuartile() { + return Double.NaN; + } + + @Override + public void merge(@Nonnull Statistic statistic) { + throw new UnsupportedOperationException(); + } + + @Override + @Nonnull + public String getDescription() { + return "Sample"; + } + + + @Nonnull + private static final String[] HEADER = new String[] { + "Count", + "Total", + "Min", + "Average", + "First Quartile", + "Median", + "Third Quartile", + "Max" + }; + + @Override + @Nonnull + public String[] getHeader() { + return HEADER.clone(); + } + + @Nonnull + public static String[] getStaticHeader() { + return HEADER.clone(); + } + + @Nonnull + public static Formatter<? extends Object>[] getStaticFormatters() { + return new Formatter<?>[] { + new LongCodec(), + new DoubleCodec(), + new DoubleCodec(), + new DoubleCodec(), + new DoubleCodec(), + new DoubleCodec(), + new DoubleCodec(), + new DoubleCodec() + }; + } + + @Override + @Nonnull + public Formatter<? extends Object>[] getFormatters() { + return getStaticFormatters(); + } + + @Override + @Nonnegative + public int getColumnCount() { + return HEADER.length; + } +} diff --git a/sched/src/com/android/sched/util/log/stats/ExtendedSampleImpl.java b/sched/src/com/android/sched/util/log/stats/ExtendedSampleImpl.java new file mode 100644 index 0000000..c0e77c4 --- /dev/null +++ b/sched/src/com/android/sched/util/log/stats/ExtendedSampleImpl.java @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sched.util.log.stats; + +import com.google.common.collect.Iterators; + +import com.android.sched.util.table.DataHeader; +import com.android.sched.util.table.DataRow; + +import java.util.Arrays; +import java.util.Iterator; + +import javax.annotation.Nonnegative; +import javax.annotation.Nonnull; + +/** + * Extended statistic computation on a set of values. Have Median, First quartile, Third quartile. + */ +public class ExtendedSampleImpl extends ExtendedSample implements DataRow, DataHeader { + private static final int INITIAL_CAPACITY = 16; + private static final int INCREMENT = 0; + + @Nonnull + protected double[] samples = new double[INITIAL_CAPACITY]; + @Nonnegative + protected int count = 0; + private double total; + private boolean isSorted = true; + + @Nonnegative + private final int increment; + + public ExtendedSampleImpl(@Nonnull StatisticId<? extends Statistic> id) { + super(id); + + this.increment = INCREMENT; + } + + @Override + public synchronized void add(double value) { + ensureCapacity(count); + + samples[count++] = value; + total += value; + isSorted = false; + } + + @Override + @Nonnegative + public int getCount() { + return count; + } + + @Override + public double getTotal() { + return total; + } + + @Override + public synchronized double getMin() { + ensureSorted(); + return samples[0]; + } + + @Override + public synchronized double getAverage() { + return total / count; + } + + @Override + public synchronized double getMax() { + ensureSorted(); + return samples[count]; + } + + @Override + public synchronized double getFirstQuartile() { + return getNth(1, 4); + } + + @Override + public synchronized double getMedian() { + return getNth(1, 2); + } + + @Override + public synchronized double getThirdQuartile() { + return getNth(3, 4); + } + + @Override + public synchronized void merge(@Nonnull Statistic statistic) { + ExtendedSampleImpl samples = (ExtendedSampleImpl) statistic; + + synchronized (samples) { + ensureCapacity(count + samples.count); + + System.arraycopy(samples.samples, 0, this.samples, count, samples.count); + count += samples.count; + total += samples.total; + isSorted = false; + } + } + + private void ensureSorted() { + if (!isSorted) { + Arrays.sort(samples, 0, count); + isSorted = true; + } + } + + private void ensureCapacity (@Nonnegative int index) { + if (index >= samples.length) { + int newLength; + + if (increment <= 0) { + newLength = samples.length * 2 + 1; + } else { + newLength = samples.length + increment; + } + + double[] newArray = new double[newLength]; + System.arraycopy(samples, 0, newArray, 0, count); + + samples = newArray; + } + } + + private double getNth(int n, int d) { + ensureSorted(); + + if (count == 0) { + return Double.NaN; + } + + if (count == 1) { + return samples[0]; + } + + double pos = (double) (n * (count + 1)) / (double) d; + + if (pos < 1.0) { + return samples[0]; + } + + double floor = Math.floor(pos); + double diff = pos - floor; + double vLow = samples[(int) pos - 1]; + + if (diff == 0) { + return vLow; + } else { + double vHigh = samples[(int) pos]; + return vLow + diff * (vHigh - vLow); + } + } + + @Override + @Nonnull + public synchronized Iterator<Object> iterator() { + ensureSorted(); + + return Iterators.<Object>forArray( + Long.valueOf(getCount()), + Double.valueOf(getTotal()), + Double.valueOf(getMin()), + Double.valueOf(getAverage()), + Double.valueOf(getFirstQuartile()), + Double.valueOf(getMedian()), + Double.valueOf(getThirdQuartile()), + Double.valueOf(getMax())); + } +} diff --git a/sched/src/com/android/sched/util/log/stats/ObjectAlloc.java b/sched/src/com/android/sched/util/log/stats/ObjectAlloc.java index ed57ae4..eb547a2 100644 --- a/sched/src/com/android/sched/util/log/stats/ObjectAlloc.java +++ b/sched/src/com/android/sched/util/log/stats/ObjectAlloc.java @@ -46,52 +46,6 @@ public class ObjectAlloc extends Statistic { @Override @Nonnull - @Deprecated - public Object getValue(@Nonnegative int columnIdx) { - throw new AssertionError(); - } - - @Override - @Nonnull - @Deprecated - public String getHumanReadableValue(@Nonnegative int columnIdx) { - throw new AssertionError(); - } - - @Override - @Nonnull - @Deprecated - public String getDescription(@Nonnegative int columnIdx) { - switch (columnIdx) { - case 0: - return "Object count"; - case 1: - return "Object size"; - case 2: - return "Total size"; - default: - throw new AssertionError(); - } - } - - @Override - @Nonnull - @Deprecated - public String getType(@Nonnegative int columnIdx) { - switch (columnIdx) { - case 0: - return "number"; - case 1: - return "number"; - case 2: - return "number"; - default: - throw new AssertionError(); - } - } - - @Override - @Nonnull public String getDescription() { return "Object allocation"; } diff --git a/sched/src/com/android/sched/util/log/stats/ObjectAllocImpl.java b/sched/src/com/android/sched/util/log/stats/ObjectAllocImpl.java index 3f511d4..eb0f887 100644 --- a/sched/src/com/android/sched/util/log/stats/ObjectAllocImpl.java +++ b/sched/src/com/android/sched/util/log/stats/ObjectAllocImpl.java @@ -18,7 +18,6 @@ package com.android.sched.util.log.stats; import com.google.common.collect.Iterators; -import com.android.sched.util.log.tracer.probe.MemoryBytesProbe; import com.android.sched.util.table.DataHeader; import com.android.sched.util.table.DataRow; @@ -71,38 +70,6 @@ public class ObjectAllocImpl extends ObjectAlloc implements DataRow, DataHeader @Override @Nonnull - @Deprecated - public synchronized Object getValue(@Nonnegative int columnIdx) { - switch (columnIdx) { - case 0: - return Long.valueOf(number); - case 1: - return Long.valueOf(size); - case 2: - return Long.valueOf(size * number); - default: - throw new AssertionError(); - } - } - - @Override - @Nonnull - @Deprecated - public synchronized String getHumanReadableValue(@Nonnegative int columnIdx) { - switch (columnIdx) { - case 0: - return Long.toString(number); - case 1: - return MemoryBytesProbe.formatBytes(size); - case 2: - return MemoryBytesProbe.formatBytes(size * number); - default: - throw new AssertionError(); - } - } - - @Override - @Nonnull public synchronized Iterator<Object> iterator() { return Iterators.<Object> forArray( Long.valueOf(number), diff --git a/sched/src/com/android/sched/util/log/stats/Percent.java b/sched/src/com/android/sched/util/log/stats/Percent.java index 86096c7..f36cd47 100644 --- a/sched/src/com/android/sched/util/log/stats/Percent.java +++ b/sched/src/com/android/sched/util/log/stats/Percent.java @@ -40,58 +40,21 @@ public class Percent extends Statistic { public void add(boolean value) { } - public double getPercent() { - return Double.NaN; - } - - @Override - public void merge(@Nonnull Statistic statistic) { + public void removeTrue() { } - @Override - @Nonnull - @Deprecated - public Object getValue(@Nonnegative int columnIdx) { - throw new AssertionError(); + public void removeFalse() { } - @Override - @Nonnull - @Deprecated - public String getHumanReadableValue(@Nonnegative int columnIdx) { - throw new AssertionError(); + public void remove(boolean value) { } - @Override - @Nonnull - @Deprecated - public String getDescription(@Nonnegative int columnIdx) { - switch (columnIdx) { - case 0: - return "Percent"; - case 1: - return "Number"; - case 2: - return "Total"; - default: - throw new AssertionError(); - } + public double getPercent() { + return Double.NaN; } @Override - @Nonnull - @Deprecated - public String getType(@Nonnegative int columnIdx) { - switch (columnIdx) { - case 0: - return "number"; - case 1: - return "number"; - case 2: - return "number"; - default: - throw new AssertionError(); - } + public void merge(@Nonnull Statistic statistic) { } @Override diff --git a/sched/src/com/android/sched/util/log/stats/PercentImpl.java b/sched/src/com/android/sched/util/log/stats/PercentImpl.java index 7f02617..99f3916 100644 --- a/sched/src/com/android/sched/util/log/stats/PercentImpl.java +++ b/sched/src/com/android/sched/util/log/stats/PercentImpl.java @@ -20,10 +20,8 @@ import com.google.common.collect.Iterators; import com.android.sched.util.table.DataRow; -import java.text.NumberFormat; import java.util.Iterator; -import javax.annotation.Nonnegative; import javax.annotation.Nonnull; @@ -59,66 +57,42 @@ public class PercentImpl extends Percent implements DataRow { } @Override - public synchronized double getPercent() { - return (double ) numTrue / (double) total; + public synchronized void removeTrue() { + this.numTrue--; + this.total--; } @Override - public synchronized void merge(@Nonnull Statistic statistic) { - PercentImpl percent = (PercentImpl) statistic; - - synchronized (percent) { - this.numTrue += percent.numTrue; - this.total += percent.total; - } + public synchronized void removeFalse() { + this.total--; } @Override - @Nonnull - @Deprecated - public synchronized Object getValue(@Nonnegative int columnIdx) { - switch (columnIdx) { - case 0: - return Double.valueOf((double ) numTrue / (double) total); - case 1: - return Long.valueOf(numTrue); - case 2: - return Long.valueOf(total); - default: - throw new AssertionError(); + public synchronized void remove(boolean value) { + if (value) { + removeTrue(); + } else { + removeFalse(); } } @Override - @Nonnull - @Deprecated - public synchronized String getHumanReadableValue(@Nonnegative int columnIdx) { - switch (columnIdx) { - case 0: - if (total == 0) { - return notANumber; - } else { - return formatter.format((double) numTrue / (double) total); - } - case 1: - return Long.toString(numTrue); - case 2: - return Long.toString(total); - default: - throw new AssertionError(); + public synchronized double getPercent() { + if (numTrue < 0 || total < 0) { + return Double.NaN; } + + return (double ) numTrue / (double) total; } - @Nonnull - @Deprecated - private static NumberFormat formatter = NumberFormat.getPercentInstance(); - @Nonnull - @Deprecated - private static String notANumber; + @Override + public synchronized void merge(@Nonnull Statistic statistic) { + PercentImpl percent = (PercentImpl) statistic; - static { - formatter.setMinimumFractionDigits(2); - notANumber = formatter.format(0).replace('0', '-'); + synchronized (percent) { + this.numTrue += percent.numTrue; + this.total += percent.total; + } } @Nonnull diff --git a/sched/src/com/android/sched/util/log/stats/Sample.java b/sched/src/com/android/sched/util/log/stats/Sample.java index 027074b..115fb03 100644 --- a/sched/src/com/android/sched/util/log/stats/Sample.java +++ b/sched/src/com/android/sched/util/log/stats/Sample.java @@ -26,7 +26,7 @@ import javax.annotation.Nonnegative; import javax.annotation.Nonnull; /** - * Simple statistic computation on a set of values. + * Simple statistic computation on a set of values when statistic is not enabled. */ public class Sample extends Statistic { protected Sample(@Nonnull StatisticId<? extends Statistic> id) { @@ -41,66 +41,36 @@ public class Sample extends Statistic { throw new AssertionError(); } - @Override - @Nonnull - @Deprecated - public Object getValue(@Nonnegative int columnIdx) { - throw new AssertionError(); + @Nonnegative + public int getCount() { + return 0; } - @Override - @Nonnull - @Deprecated - public String getHumanReadableValue(@Nonnegative int columnIdx) { - throw new AssertionError(); + public double getTotal() { + return 0; } - @Override - @Nonnull - @Deprecated - public String getDescription(@Nonnegative int columnIdx) { - switch (columnIdx) { - case 0: - return "Count"; - case 1: - return "Total"; - case 2: - return "Min"; - case 3: - return "Average"; - case 4: - return "Max"; - case 5: - return "Min Marker"; - case 6: - return "Max Marker"; - default: - throw new AssertionError(); - } + public double getMin() { + return Double.NaN; } - @Override - @Nonnull - @Deprecated - public String getType(@Nonnegative int columnIdx) { - switch (columnIdx) { - case 0: - return "number"; - case 1: - return "number"; - case 2: - return "number"; - case 3: - return "number"; - case 4: - return "number"; - case 5: - return "string"; - case 6: - return "string"; - default: - throw new AssertionError(); - } + public double getAverage() { + return Double.NaN; + } + + public double getMax() { + return Double.NaN; + } + + @CheckForNull + public Object getMinObject() { + return null; + + } + + @CheckForNull + public Object getMaxObject() { + return null; } @Override @@ -110,7 +80,7 @@ public class Sample extends Statistic { } - @Nonnegative + @Nonnull private static final String[] HEADER = new String[] { "Count", "Total", diff --git a/sched/src/com/android/sched/util/log/stats/SampleImpl.java b/sched/src/com/android/sched/util/log/stats/SampleImpl.java index 3a51c1a..50d609b 100644 --- a/sched/src/com/android/sched/util/log/stats/SampleImpl.java +++ b/sched/src/com/android/sched/util/log/stats/SampleImpl.java @@ -31,7 +31,8 @@ import javax.annotation.Nonnull; * Simple statistic computation on a set of values. */ public class SampleImpl extends Sample implements DataRow, DataHeader { - private long count; + @Nonnegative + private int count; private double min = Double.POSITIVE_INFINITY; @CheckForNull @@ -64,6 +65,46 @@ public class SampleImpl extends Sample implements DataRow, DataHeader { count++; } + + @Override + @Nonnegative + public int getCount() { + return count; + } + + @Override + public double getTotal() { + return total; + } + + @Override + public double getMin() { + return min; + } + + @Override + public synchronized double getAverage() { + return total / count; + } + + @Override + public double getMax() { + return max; + } + + @Override + @CheckForNull + public Object getMinObject() { + return minObject; + + } + + @Override + @CheckForNull + public Object getMaxObject() { + return maxObject; + } + @Override public synchronized void merge(@Nonnull Statistic statistic) { SampleImpl samples = (SampleImpl) statistic; @@ -82,94 +123,14 @@ public class SampleImpl extends Sample implements DataRow, DataHeader { @Nonnull @Override - @Deprecated - public Object getValue(@Nonnegative int columnIdx) { - switch (columnIdx) { - case 0: - return Long.valueOf(count); - case 1: - return Double.valueOf(total); - case 2: - if (min == Double.POSITIVE_INFINITY) { - return Double.valueOf(Double.MAX_VALUE); - } else { - return Double.valueOf(min); - } - case 3: - if (count == 0) { - return Long.valueOf(0); - } else { - return Double.valueOf(total / count); - } - case 4: - if (max == Double.NEGATIVE_INFINITY) { - return Double.valueOf(Double.MIN_VALUE); - } else { - return Double.valueOf(max); - } - case 5: - return ""; - case 6: - return ""; - default: - throw new AssertionError(); - } - } - - @Nonnull - @Override - @Deprecated - public String getHumanReadableValue(@Nonnegative int columnIdx) { - switch (columnIdx) { - case 0: - return Long.toString(count); - case 1: - return Double.toString(total); - case 2: - if (min == Double.POSITIVE_INFINITY) { - return "--"; - } else { - return Double.toString(min); - } - case 3: - if (count == 0) { - return "--"; - } else { - return Double.toString(total / count); - } - case 4: - if (max == Double.NEGATIVE_INFINITY) { - return "--"; - } else { - return Double.toString(max); - } - case 5: - if (minObject == null) { - return "--"; - } else { - return minObject.toString(); - } - case 6: - if (maxObject == null) { - return "--"; - } else { - return maxObject.toString(); - } - default: - throw new AssertionError(); - } - } - - @Nonnull - @Override - public Iterator<Object> iterator() { + public synchronized Iterator<Object> iterator() { return Iterators.forArray( - Long.valueOf(count), - Double.valueOf(total), - Double.valueOf(min), - Double.valueOf(total / count), - Double.valueOf(max), - minObject, - maxObject); + Integer.valueOf(getCount()), + Double.valueOf(getTotal()), + Double.valueOf(getMin()), + Double.valueOf(getAverage()), + Double.valueOf(getMax()), + getMinObject(), + getMaxObject()); } } diff --git a/sched/src/com/android/sched/util/log/stats/Statistic.java b/sched/src/com/android/sched/util/log/stats/Statistic.java index 569d85c..93d2535 100644 --- a/sched/src/com/android/sched/util/log/stats/Statistic.java +++ b/sched/src/com/android/sched/util/log/stats/Statistic.java @@ -16,7 +16,12 @@ package com.android.sched.util.log.stats; +import com.google.common.collect.Iterators; + +import com.android.sched.util.codec.Formatter; +import com.android.sched.util.codec.ToStringFormatter; import com.android.sched.util.table.DataHeader; +import com.android.sched.util.table.DataRow; import javax.annotation.Nonnegative; import javax.annotation.Nonnull; @@ -40,27 +45,50 @@ public abstract class Statistic implements DataHeader { } @Nonnull - @Deprecated - public abstract Object getValue(@Nonnegative int columnIdx); + public abstract String getDescription(); + @Override @Nonnull - @Deprecated - public abstract String getHumanReadableValue(@Nonnegative int columnIdx); + public String toString() { + return id.getName(); + } + + // + // Adapter for deprecated API + // @Nonnull @Deprecated - public abstract String getDescription(@Nonnegative int columnIdx); + public final String getDescription(int columnIdx) { + return getHeader()[columnIdx]; + } @Nonnull @Deprecated - public abstract String getType(@Nonnegative int columnIdx); + public final String getType(int columnIdx) { + if (getFormatters()[columnIdx] instanceof ToStringFormatter) { + return "string"; + } else { + return "number"; + } + } @Nonnull - public abstract String getDescription(); + @Deprecated + public final Object getValue(@Nonnegative int columnIdx) { + if (this instanceof DataRow) { + DataRow data = (DataRow) this; - @Override + return Iterators.get(data.iterator(), columnIdx); + } + + throw new AssertionError(); + } + + @SuppressWarnings("unchecked") @Nonnull - public String toString() { - return id.getName(); + @Deprecated + public final String getHumanReadableValue(@Nonnegative int columnIdx) { + return ((Formatter<Object>) (getFormatters()[columnIdx])).formatValue(getValue(columnIdx)); } } diff --git a/sched/src/com/android/sched/util/log/stats/StatisticId.java b/sched/src/com/android/sched/util/log/stats/StatisticId.java index c6f0fe5..597a5a3 100644 --- a/sched/src/com/android/sched/util/log/stats/StatisticId.java +++ b/sched/src/com/android/sched/util/log/stats/StatisticId.java @@ -18,12 +18,8 @@ package com.android.sched.util.log.stats; import com.android.sched.util.config.ReflectFactory; -import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; import java.util.Map; -import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import javax.annotation.Nonnull; @@ -35,9 +31,6 @@ import javax.annotation.Nonnull; */ public class StatisticId<T extends Statistic> { @Nonnull - private static Set<StatisticId<? extends Statistic>> ids = - Collections.synchronizedSet(new HashSet<StatisticId<? extends Statistic>>()); - @Nonnull private static Map<Class<? extends Statistic>, Statistic> dummies = new ConcurrentHashMap<Class<? extends Statistic>, Statistic>(); @Nonnull @@ -70,8 +63,6 @@ public class StatisticId<T extends Statistic> { dummies.put(dummyClass, newDummyInstance()); regulars.put(dummyClass, regularClass); } - - ids.add(this); } @Nonnull @@ -96,12 +87,6 @@ public class StatisticId<T extends Statistic> { @Nonnull @Deprecated - public static synchronized Collection<StatisticId<? extends Statistic>> getIds() { - return new ArrayList<StatisticId<? extends Statistic>>(ids); - } - - @Nonnull - @Deprecated public static synchronized Collection<? extends Statistic> getDummies() { return dummies.values(); } diff --git a/sched/src/com/android/sched/util/log/tracer/DynamicEventType.java b/sched/src/com/android/sched/util/log/tracer/DynamicEventType.java index 2f2ca1b..c9f43ca 100644 --- a/sched/src/com/android/sched/util/log/tracer/DynamicEventType.java +++ b/sched/src/com/android/sched/util/log/tracer/DynamicEventType.java @@ -16,7 +16,6 @@ package com.android.sched.util.log.tracer; -import com.android.sched.util.Colors; import com.android.sched.util.log.EventType; import javax.annotation.Nonnull; @@ -26,19 +25,10 @@ import javax.annotation.Nonnull; */ class DynamicEventType implements EventType { @Nonnull - private final String cssColor; - @Nonnull private final String name; DynamicEventType(@Nonnull String name) { this.name = name; - this.cssColor = Colors.getCssColor(Colors.getRandomPastel(name.hashCode())); - } - - @Override - @Nonnull - public String getColor() { - return cssColor; } @Override diff --git a/sched/src/com/android/sched/util/log/tracer/TracerEventType.java b/sched/src/com/android/sched/util/log/tracer/TracerEventType.java index 59d825d..3558a45 100644 --- a/sched/src/com/android/sched/util/log/tracer/TracerEventType.java +++ b/sched/src/com/android/sched/util/log/tracer/TracerEventType.java @@ -24,24 +24,16 @@ import javax.annotation.Nonnull; * Represents a type of event whose performance is tracked. */ public enum TracerEventType implements EventType { - OVERHEAD("Tracer overhead", "Plum"), - NOEVENT("No Event", "Black"), - NOTYPE("Not a Type", "Black"); + OVERHEAD("Tracer overhead"), + NOEVENT("No Event"), + SINGLETON("Singleton event"), + NOTYPE("Not a Type"); @Nonnull - private final String cssColor; - @Nonnull private final String name; - TracerEventType(@Nonnull String name, @Nonnull String cssColor) { + TracerEventType(@Nonnull String name) { this.name = name; - this.cssColor = cssColor; - } - - @Override - @Nonnull - public String getColor() { - return cssColor; } @Override diff --git a/sched/src/com/android/sched/util/log/tracer/watcher/AllocationWatcher.java b/sched/src/com/android/sched/util/log/tracer/watcher/AllocationWatcher.java index 09a8e18..78fd9b8 100644 --- a/sched/src/com/android/sched/util/log/tracer/watcher/AllocationWatcher.java +++ b/sched/src/com/android/sched/util/log/tracer/watcher/AllocationWatcher.java @@ -22,23 +22,17 @@ import com.android.sched.util.log.Tracer; import com.android.sched.util.log.TracerFactory; import com.android.sched.util.log.stats.Alloc; import com.android.sched.util.log.stats.AllocImpl; -import com.android.sched.util.log.stats.ArrayAlloc; -import com.android.sched.util.log.stats.ArrayAllocImpl; -import com.android.sched.util.log.stats.ObjectAlloc; -import com.android.sched.util.log.stats.ObjectAllocImpl; import com.android.sched.util.log.stats.Statistic; import com.android.sched.util.log.stats.StatisticId; import java.util.Iterator; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; import javax.annotation.CheckForNull; import javax.annotation.Nonnegative; import javax.annotation.Nonnull; /** - * Class to watch {@link Object} creation. + * Class to watch {@link Object} creation and make a global statistic about allocation. */ public class AllocationWatcher implements ObjectWatcher<Object> { static class Statistics implements ObjectWatcher.Statistics { @@ -54,14 +48,6 @@ public class AllocationWatcher implements ObjectWatcher<Object> { "Total object and array allocations", AllocImpl.class, Alloc.class); - @Nonnull - private static final Map<Class<?>, StatisticId<ObjectAlloc>> objectStats = - new ConcurrentHashMap<Class<?>, StatisticId<ObjectAlloc>>(); - - @Nonnull - private static final Map<Class<?>, StatisticId<ArrayAlloc>> arrayStats = - new ConcurrentHashMap<Class<?>, StatisticId<ArrayAlloc>>(); - @Override public boolean notifyInstantiation( @Nonnull Object object, @Nonnegative long size, int count, @Nonnull EventType notUsed) { @@ -77,46 +63,19 @@ public class AllocationWatcher implements ObjectWatcher<Object> { } private void notifyObject(@Nonnull Class<?> type, @Nonnegative long size) { - synchronized (AllocationWatcher.class) { - StatisticId<ObjectAlloc> id = objectStats.get(type); - if (id == null) { - String name = type.getName(); - - id = new StatisticId<ObjectAlloc>("jack.allocation.object." + name, - "Object allocation of type " + type.getName(), ObjectAllocImpl.class, - ObjectAlloc.class); - objectStats.put(type, id); - } - - try { - Tracer tracer = TracerFactory.getTracer(); - tracer.getStatistic(id).recordObjectAllocation(size); - tracer.getStatistic(ALLOCATIONS).recordAllocation(size); - } catch (RuntimeException e) { - e.printStackTrace(); - } + try { + TracerFactory.getTracer().getStatistic(ALLOCATIONS).recordAllocation(size); + } catch (RuntimeException e) { + // Do best effort here } } private synchronized void notifyArray(@Nonnull Class<?> type, @Nonnegative long size, @Nonnegative int count) { - synchronized (AllocationWatcher.class) { - StatisticId<ArrayAlloc> id = arrayStats.get(type); - if (id == null) { - String name = type.getName(); - - id = new StatisticId<ArrayAlloc>("jack.allocation.array." + name, - "Array allocation of type " + type.getName(), ArrayAllocImpl.class, ArrayAlloc.class); - arrayStats.put(type, id); - } - - try { - Tracer tracer = TracerFactory.getTracer(); - tracer.getStatistic(id).recordObjectAllocation(count, size); - tracer.getStatistic(ALLOCATIONS).recordAllocation(size); - } catch (RuntimeException e) { - e.printStackTrace(); - } + try { + TracerFactory.getTracer().getStatistic(ALLOCATIONS).recordAllocation(size); + } catch (RuntimeException e) { + // Do best effort here } } diff --git a/sched/src/com/android/sched/util/log/tracer/watcher/DetailedAllocationWatcher.java b/sched/src/com/android/sched/util/log/tracer/watcher/DetailedAllocationWatcher.java new file mode 100644 index 0000000..f8a97e3 --- /dev/null +++ b/sched/src/com/android/sched/util/log/tracer/watcher/DetailedAllocationWatcher.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sched.util.log.tracer.watcher; + +import com.android.sched.util.codec.ImplementationName; +import com.android.sched.util.log.EventType; +import com.android.sched.util.log.Tracer; +import com.android.sched.util.log.TracerFactory; +import com.android.sched.util.log.stats.ArrayAlloc; +import com.android.sched.util.log.stats.ArrayAllocImpl; +import com.android.sched.util.log.stats.ObjectAlloc; +import com.android.sched.util.log.stats.ObjectAllocImpl; +import com.android.sched.util.log.stats.Statistic; +import com.android.sched.util.log.stats.StatisticId; + +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnegative; +import javax.annotation.Nonnull; + +/** + * Class to watch {@link Object} creation and make detailed statistics about allocation. + */ +public class DetailedAllocationWatcher implements ObjectWatcher<Object> { + static class Statistics implements ObjectWatcher.Statistics { + @Override + public Iterator<Statistic> iterator() { + throw new AssertionError(); + } + } + + @Nonnull + private static final Map<Class<?>, StatisticId<ObjectAlloc>> objectStats = + new ConcurrentHashMap<Class<?>, StatisticId<ObjectAlloc>>(); + + @Nonnull + private static final Map<Class<?>, StatisticId<ArrayAlloc>> arrayStats = + new ConcurrentHashMap<Class<?>, StatisticId<ArrayAlloc>>(); + + @Override + public boolean notifyInstantiation( + @Nonnull Object object, @Nonnegative long size, int count, @Nonnull EventType notUsed) { + Class<?> type = object.getClass(); + + if (count == -1) { + notifyObject(type, size); + } else { + notifyArray(type, size, count); + } + + return false; + } + + private void notifyObject(@Nonnull Class<?> type, @Nonnegative long size) { + StatisticId<ObjectAlloc> id; + + synchronized (DetailedAllocationWatcher.class) { + id = objectStats.get(type); + if (id == null) { + String name = type.getName(); + + id = new StatisticId<ObjectAlloc>("jack.allocation.object." + name, + "Object allocation of type " + name, ObjectAllocImpl.class, + ObjectAlloc.class); + objectStats.put(type, id); + } + } + + try { + TracerFactory.getTracer().getStatistic(id).recordObjectAllocation(size); + } catch (RuntimeException e) { + // Do best effort here + } + } + + private synchronized void notifyArray(@Nonnull Class<?> type, @Nonnegative long size, + @Nonnegative int count) { + StatisticId<ArrayAlloc> id; + + synchronized (DetailedAllocationWatcher.class) { + id = arrayStats.get(type); + if (id == null) { + String name = type.getName(); + + id = new StatisticId<ArrayAlloc>("jack.allocation.array." + name, + "Array allocation of type " + name, ArrayAllocImpl.class, ArrayAlloc.class); + arrayStats.put(type, id); + } + } + + try { + TracerFactory.getTracer().getStatistic(id).recordObjectAllocation(count, size); + } catch (RuntimeException e) { + // Do best effort here + } + } + + @Override + @Nonnull + public ObjectWatcher.Statistics addSample(@Nonnull Object node, + @CheckForNull ObjectWatcher.Statistics raw, @Nonnull EventType type) { + throw new AssertionError(); + } + + /** + * Install a {@link DetailedAllocationWatcher} + */ + @ImplementationName(iface = WatcherInstaller.class, name = "detailed-object-alloc") + public static class DetailedAllocationWatcherInstaller implements WatcherInstaller { + @Override + public void install(@Nonnull Tracer tracer) { + tracer.registerWatcher(Object.class, DetailedAllocationWatcher.class); + } + } +} diff --git a/sched/src/com/android/sched/vfs/AbstractVElement.java b/sched/src/com/android/sched/vfs/AbstractVElement.java new file mode 100644 index 0000000..939f8bc --- /dev/null +++ b/sched/src/com/android/sched/vfs/AbstractVElement.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sched.vfs; + +import javax.annotation.Nonnull; + +/** + * Base class for implementing VElement. + */ +public abstract class AbstractVElement implements VElement { + + @Nonnull + @Override + public String toString() { + return getLocation().getDescription(); + } +} diff --git a/sched/src/com/android/sched/vfs/InputVDir.java b/sched/src/com/android/sched/vfs/InputVDir.java new file mode 100644 index 0000000..f57636d --- /dev/null +++ b/sched/src/com/android/sched/vfs/InputVDir.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sched.vfs; + +import java.util.Collection; + +import javax.annotation.Nonnull; + +/** + * Virtual directory. + */ +public interface InputVDir extends VElement { + + @Nonnull + Collection<? extends VElement> list(); + +} diff --git a/sched/src/com/android/sched/vfs/InputVFile.java b/sched/src/com/android/sched/vfs/InputVFile.java new file mode 100644 index 0000000..3d7d2cd --- /dev/null +++ b/sched/src/com/android/sched/vfs/InputVFile.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sched.vfs; + +import java.io.IOException; +import java.io.InputStream; + +import javax.annotation.Nonnull; + +/** + * Virtual file. + */ +public interface InputVFile extends VElement { + + @Nonnull + InputStream openRead() throws IOException; + +} diff --git a/sched/src/com/android/sched/vfs/OutputVDir.java b/sched/src/com/android/sched/vfs/OutputVDir.java new file mode 100644 index 0000000..622c553 --- /dev/null +++ b/sched/src/com/android/sched/vfs/OutputVDir.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sched.vfs; + +import java.io.IOException; + +import javax.annotation.Nonnull; + +/** + * Virtual directory to write to. + */ +public interface OutputVDir extends VElement { + + @Nonnull + OutputVFile createOutputVFile(@Nonnull String filePath) throws IOException; + +} diff --git a/sched/src/com/android/sched/vfs/OutputVFile.java b/sched/src/com/android/sched/vfs/OutputVFile.java new file mode 100644 index 0000000..32154b5 --- /dev/null +++ b/sched/src/com/android/sched/vfs/OutputVFile.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sched.vfs; + +import java.io.IOException; +import java.io.OutputStream; + +import javax.annotation.Nonnull; + +/** + * Virtual file to write to. + */ +public interface OutputVFile extends VElement { + + @Nonnull + OutputStream openWrite() throws IOException; + +} diff --git a/sched/src/com/android/sched/vfs/VElement.java b/sched/src/com/android/sched/vfs/VElement.java new file mode 100644 index 0000000..748b9bd --- /dev/null +++ b/sched/src/com/android/sched/vfs/VElement.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sched.vfs; + +import com.android.sched.util.config.Location; + +import javax.annotation.Nonnull; + +/** + * Element of a virtual file system. + */ +public interface VElement { + + @Nonnull + String getName(); + + @Nonnull + public Location getLocation(); + +} diff --git a/sched/src/com/android/sched/vfs/direct/InputDirectDir.java b/sched/src/com/android/sched/vfs/direct/InputDirectDir.java new file mode 100644 index 0000000..4e88043 --- /dev/null +++ b/sched/src/com/android/sched/vfs/direct/InputDirectDir.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sched.vfs.direct; + +import com.android.sched.util.ConcurrentIOException; +import com.android.sched.util.config.FileLocation; +import com.android.sched.util.config.Location; +import com.android.sched.util.file.NotFileOrDirectoryException; +import com.android.sched.vfs.AbstractVElement; +import com.android.sched.vfs.InputVDir; +import com.android.sched.vfs.VElement; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; + +/** + * Directory in the file system. + */ +public class InputDirectDir extends AbstractVElement implements InputVDir { + + @Nonnull + private final File dir; + @CheckForNull + private ArrayList<VElement> list; + + public InputDirectDir(@Nonnull File dir) throws NotFileOrDirectoryException { + if (!dir.isDirectory()) { + throw new NotFileOrDirectoryException(dir.getAbsolutePath(), false); + } + this.dir = dir; + } + + @Nonnull + @Override + public String getName() { + return dir.getName(); + } + + @Nonnull + @Override + public synchronized Collection<? extends VElement> list() { + if (list == null) { + File[] subs = dir.listFiles(); + if (subs == null) { + throw new ConcurrentIOException(new ListDirException(dir)); + } + if (subs.length == 0) { + return Collections.emptyList(); + } + + list = new ArrayList<VElement>(subs.length); + for (File sub : subs) { + try { + if (sub.isFile()) { + list.add(new InputDirectFile(sub)); + } else { + list.add(new InputDirectDir(sub)); + } + } catch (NotFileOrDirectoryException e) { + throw new ConcurrentIOException(e); + } + } + } + + assert list != null; + return list; + } + + @Override + @Nonnull + public Location getLocation() { + return new FileLocation(dir); + } +} diff --git a/sched/src/com/android/sched/vfs/direct/InputDirectFile.java b/sched/src/com/android/sched/vfs/direct/InputDirectFile.java new file mode 100644 index 0000000..45ae9ab --- /dev/null +++ b/sched/src/com/android/sched/vfs/direct/InputDirectFile.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sched.vfs.direct; + +import com.android.sched.util.config.FileLocation; +import com.android.sched.util.config.Location; +import com.android.sched.util.file.NotFileOrDirectoryException; +import com.android.sched.vfs.AbstractVElement; +import com.android.sched.vfs.InputVFile; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; + +import javax.annotation.Nonnull; + +/** + * A {@code VFile} directly backed but a {@code java.io.File}. + */ +public class InputDirectFile extends AbstractVElement implements InputVFile { + + @Nonnull + private final File file; + + public InputDirectFile(@Nonnull File file) throws NotFileOrDirectoryException { + if (!file.isFile()) { + throw new NotFileOrDirectoryException(file.getAbsolutePath(), true); + } + this.file = file; + } + + @Nonnull + @Override + public InputStream openRead() throws FileNotFoundException { + return new FileInputStream(file); + } + + @Nonnull + @Override + public String getName() { + return file.getName(); + } + + @Override + @Nonnull + public Location getLocation() { + return new FileLocation(file); + } +} diff --git a/sched/src/com/android/sched/vfs/direct/ListDirException.java b/sched/src/com/android/sched/vfs/direct/ListDirException.java new file mode 100644 index 0000000..802c9d4 --- /dev/null +++ b/sched/src/com/android/sched/vfs/direct/ListDirException.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sched.vfs.direct; + +import java.io.File; +import java.io.IOException; + +import javax.annotation.Nonnull; + +/** + * Thrown when listing a directory content failed. + */ +public class ListDirException extends IOException { + + private static final long serialVersionUID = 1L; + @Nonnull + private final File dir; + + public ListDirException(@Nonnull File dir) { + this.dir = dir; + } + + @Override + public String getMessage() { + return "Failed to list directory content '" + dir.getPath() + "'"; + } +} diff --git a/sched/src/com/android/sched/vfs/direct/OutputDirectDir.java b/sched/src/com/android/sched/vfs/direct/OutputDirectDir.java new file mode 100644 index 0000000..58968b1 --- /dev/null +++ b/sched/src/com/android/sched/vfs/direct/OutputDirectDir.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sched.vfs.direct; + +import com.android.sched.util.config.FileLocation; +import com.android.sched.util.config.Location; +import com.android.sched.util.file.CannotCreateFileException; +import com.android.sched.util.file.Directory; +import com.android.sched.util.file.FileAlreadyExistsException; +import com.android.sched.vfs.AbstractVElement; +import com.android.sched.vfs.OutputVDir; +import com.android.sched.vfs.OutputVFile; + +import java.io.File; + +import javax.annotation.Nonnull; + +/** + * An {@link OutputVDir} using a real directory. + */ +public class OutputDirectDir extends AbstractVElement implements OutputVDir { + + @Nonnull + private final File dir; + + public OutputDirectDir(@Nonnull Directory dir) { + this.dir = dir.getFile(); + } + + @Nonnull + @Override + public String getName() { + return dir.getName(); + } + + @Override + @Nonnull + public Location getLocation() { + return new FileLocation(dir); + } + + @Override + @Nonnull + public OutputVFile createOutputVFile(@Nonnull String filePath) throws CannotCreateFileException, + FileAlreadyExistsException { + File file = new File(dir, filePath); + if (!file.getParentFile().mkdirs() && !file.getParentFile().isDirectory()) { + throw new CannotCreateFileException(file.getParentFile().getAbsolutePath(), + /* isFile */ false); + } + return new OutputDirectFile(file); + } +} diff --git a/sched/src/com/android/sched/vfs/direct/OutputDirectFile.java b/sched/src/com/android/sched/vfs/direct/OutputDirectFile.java new file mode 100644 index 0000000..58b36e9 --- /dev/null +++ b/sched/src/com/android/sched/vfs/direct/OutputDirectFile.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sched.vfs.direct; + +import com.android.sched.util.config.FileLocation; +import com.android.sched.util.config.Location; +import com.android.sched.util.file.FileAlreadyExistsException; +import com.android.sched.vfs.AbstractVElement; +import com.android.sched.vfs.OutputVFile; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.OutputStream; + +import javax.annotation.Nonnull; + +/** + * An {@link OutputVFile} directly backed by a {@link File} directory. + */ +class OutputDirectFile extends AbstractVElement implements OutputVFile { + + private static boolean checkIfFileAlreadyExists = false; + + @Nonnull + private final File file; + + public OutputDirectFile(@Nonnull File file) throws FileAlreadyExistsException { + if (checkIfFileAlreadyExists && file.exists()) { + throw new FileAlreadyExistsException(file.getAbsolutePath(), !file.isDirectory()); + } + this.file = file; + } + + @Nonnull + @Override + public OutputStream openWrite() throws FileNotFoundException { + return new FileOutputStream(file); + } + + @Nonnull + @Override + public String getName() { + return file.getPath(); + } + + @Override + @Nonnull + public Location getLocation() { + return new FileLocation(file); + } +} diff --git a/sched/src/com/android/sched/vfs/zip/InputZipArchive.java b/sched/src/com/android/sched/vfs/zip/InputZipArchive.java new file mode 100644 index 0000000..fec4e36 --- /dev/null +++ b/sched/src/com/android/sched/vfs/zip/InputZipArchive.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sched.vfs.zip; + +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.util.Enumeration; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import javax.annotation.Nonnull; + +/** + * Virtual directory for viewing the content of a zip file. + */ +public class InputZipArchive extends InputZipVDir implements Closeable { + + @Nonnull + public static final String IN_ZIP_SEPARATOR = "/"; + + @Nonnull + private final ZipFile zip; + + public InputZipArchive(@Nonnull File zipFile) throws IOException { + super("", zipFile, new ZipEntry("")); + + zip = new ZipFile(zipFile); + + for (Enumeration<? extends ZipEntry> entries = zip.entries(); entries.hasMoreElements();) { + ZipEntry entry = entries.nextElement(); + if (!entry.isDirectory()) { + String entryName = entry.getName(); + String[] names = entryName.split(IN_ZIP_SEPARATOR); + @SuppressWarnings("resource") + InputZipVDir dir = this; + StringBuilder inZipPath = new StringBuilder(); + for (int i = 0; i < names.length - 1; i++) { + String simpleName = names[i]; + inZipPath.append(IN_ZIP_SEPARATOR).append(simpleName); + InputZipVDir nextDir = (InputZipVDir) dir.subs.get(simpleName); + if (nextDir == null) { + nextDir = new InputZipVDir(simpleName, zipFile, new ZipEntry(inZipPath.toString())); + dir.subs.put(simpleName, nextDir); + } + dir = nextDir; + } + String simpleName = names[names.length - 1]; + dir.subs.put(simpleName, new InputZipVFile(simpleName, zip, entry)); + } + } + } + + @Override + public void close() throws IOException { + zip.close(); + } +} diff --git a/sched/src/com/android/sched/vfs/zip/InputZipVDir.java b/sched/src/com/android/sched/vfs/zip/InputZipVDir.java new file mode 100644 index 0000000..7ad1cc5 --- /dev/null +++ b/sched/src/com/android/sched/vfs/zip/InputZipVDir.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sched.vfs.zip; + +import com.android.sched.util.config.FileLocation; +import com.android.sched.util.config.Location; +import com.android.sched.util.config.ZipLocation; +import com.android.sched.vfs.AbstractVElement; +import com.android.sched.vfs.InputVDir; +import com.android.sched.vfs.VElement; + +import java.io.File; +import java.util.Collection; +import java.util.HashMap; +import java.util.zip.ZipEntry; + +import javax.annotation.Nonnull; + +class InputZipVDir extends AbstractVElement implements InputVDir { + + @Nonnull + protected final HashMap<String, VElement> subs = new HashMap<String, VElement>(); + @Nonnull + private final String name; + + @Nonnull + private final Location location; + + InputZipVDir(@Nonnull String name, @Nonnull File zip, @Nonnull ZipEntry entry) { + this.name = name; + this.location = new ZipLocation(new FileLocation(zip), entry); + } + + @Nonnull + @Override + public String getName() { + return name; + } + + @Nonnull + @Override + public Collection<? extends VElement> list() { + return subs.values(); + } + + @Override + @Nonnull + public Location getLocation() { + return location; + } + +} diff --git a/sched/src/com/android/sched/vfs/zip/InputZipVFile.java b/sched/src/com/android/sched/vfs/zip/InputZipVFile.java new file mode 100644 index 0000000..090dc2f --- /dev/null +++ b/sched/src/com/android/sched/vfs/zip/InputZipVFile.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sched.vfs.zip; + +import com.android.sched.util.config.FileLocation; +import com.android.sched.util.config.Location; +import com.android.sched.util.config.ZipLocation; +import com.android.sched.vfs.AbstractVElement; +import com.android.sched.vfs.InputVFile; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import javax.annotation.Nonnull; + +class InputZipVFile extends AbstractVElement implements InputVFile { + + @Nonnull + private final String name; + @Nonnull + private final ZipFile zip; + @Nonnull + private final ZipEntry entry; + + InputZipVFile(@Nonnull String name, @Nonnull ZipFile zip, @Nonnull ZipEntry entry) { + this.name = name; + this.zip = zip; + this.entry = entry; + } + + @Nonnull + @Override + public String getName() { + return name; + } + + @Nonnull + @Override + public InputStream openRead() throws IOException { + return zip.getInputStream(entry); + } + + @Override + @Nonnull + public Location getLocation() { + return new ZipLocation(new FileLocation(new File(zip.getName())), entry); + } + +} diff --git a/sched/src/com/android/sched/vfs/zip/OutputZipRootVDir.java b/sched/src/com/android/sched/vfs/zip/OutputZipRootVDir.java new file mode 100644 index 0000000..b1b6ae1 --- /dev/null +++ b/sched/src/com/android/sched/vfs/zip/OutputZipRootVDir.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sched.vfs.zip; + +import com.android.sched.util.config.FileLocation; +import com.android.sched.util.config.Location; +import com.android.sched.util.config.ZipLocation; +import com.android.sched.vfs.AbstractVElement; +import com.android.sched.vfs.OutputVDir; +import com.android.sched.vfs.OutputVFile; +import com.android.sched.vfs.VElement; + +import java.io.Closeable; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.HashMap; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import javax.annotation.Nonnull; + +/** + * A root {@link OutputVDir} backed by a zip archive. + */ +public class OutputZipRootVDir extends AbstractVElement implements OutputVDir, Closeable { + + @Nonnull + protected final HashMap<String, VElement> subs = new HashMap<String, VElement>(); + @Nonnull + private final Location location; + @Nonnull + protected final ZipOutputStream zos; + @Nonnull + private final File zipFile; + + public OutputZipRootVDir(@Nonnull File zipFile) throws FileNotFoundException { + this.zipFile = zipFile; + location = new ZipLocation(new FileLocation(zipFile), new ZipEntry("")); + zos = new ZipOutputStream(new FileOutputStream(zipFile)); + } + + @Nonnull + @Override + public String getName() { + return zipFile.getName(); + } + + @Override + @Nonnull + public Location getLocation() { + return location; + } + + @Override + @Nonnull + public OutputVFile createOutputVFile(@Nonnull String filePath) { + return new OutputZipVFile(zos, new ZipEntry(filePath), zipFile); + } + + @Override + public void close() throws IOException { + zos.close(); + } +} diff --git a/sched/src/com/android/sched/vfs/zip/OutputZipVFile.java b/sched/src/com/android/sched/vfs/zip/OutputZipVFile.java new file mode 100644 index 0000000..afee54d --- /dev/null +++ b/sched/src/com/android/sched/vfs/zip/OutputZipVFile.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sched.vfs.zip; + +import com.android.sched.util.config.FileLocation; +import com.android.sched.util.config.Location; +import com.android.sched.util.config.ZipLocation; +import com.android.sched.util.stream.UncloseableOutputStream; +import com.android.sched.vfs.AbstractVElement; +import com.android.sched.vfs.OutputVFile; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import javax.annotation.Nonnull; + +class OutputZipVFile extends AbstractVElement implements OutputVFile { + + @Nonnull + private final ZipOutputStream zos; + @Nonnull + private final ZipEntry entry; + @Nonnull + private final Location location; + + OutputZipVFile(@Nonnull ZipOutputStream zos, @Nonnull ZipEntry entry, @Nonnull File zipFile) { + this.zos = zos; + this.entry = entry; + location = new ZipLocation(new FileLocation(zipFile), entry); + } + + @Nonnull + @Override + public String getName() { + return entry.getName(); + } + + @Nonnull + @Override + public OutputStream openWrite() throws IOException { + zos.putNextEntry(entry); + return new UncloseableOutputStream(zos); + } + + @Override + @Nonnull + public Location getLocation() { + return location; + } + +} |