From 3635740f2fcb24192cfa20a3d8d30cbc690c64d8 Mon Sep 17 00:00:00 2001 From: "Brian S. O'Neill" Date: Wed, 30 Aug 2006 01:46:24 +0000 Subject: Add standard adapters --- .../carbonado/adapter/AdapterDefinition.java | 124 +++++++++++ .../amazon/carbonado/adapter/DateTimeAdapter.java | 220 +++++++++++++++++++ .../com/amazon/carbonado/adapter/TextAdapter.java | 244 +++++++++++++++++++++ .../amazon/carbonado/adapter/TrueFalseAdapter.java | 226 +++++++++++++++++++ .../com/amazon/carbonado/adapter/YesNoAdapter.java | 226 +++++++++++++++++++ .../com/amazon/carbonado/adapter/package-info.java | 24 ++ 6 files changed, 1064 insertions(+) create mode 100644 src/main/java/com/amazon/carbonado/adapter/AdapterDefinition.java create mode 100644 src/main/java/com/amazon/carbonado/adapter/DateTimeAdapter.java create mode 100644 src/main/java/com/amazon/carbonado/adapter/TextAdapter.java create mode 100644 src/main/java/com/amazon/carbonado/adapter/TrueFalseAdapter.java create mode 100644 src/main/java/com/amazon/carbonado/adapter/YesNoAdapter.java create mode 100644 src/main/java/com/amazon/carbonado/adapter/package-info.java diff --git a/src/main/java/com/amazon/carbonado/adapter/AdapterDefinition.java b/src/main/java/com/amazon/carbonado/adapter/AdapterDefinition.java new file mode 100644 index 0000000..0787010 --- /dev/null +++ b/src/main/java/com/amazon/carbonado/adapter/AdapterDefinition.java @@ -0,0 +1,124 @@ +/* + * Copyright 2006 Amazon Technologies, Inc. or its affiliates. + * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks + * of Amazon Technologies, Inc. or its affiliates. All rights reserved. + * + * 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.amazon.carbonado.adapter; + +import java.lang.annotation.*; + +/** + * Allows annotations to be defined for supporting property types which are not + * natively supported by the underlying storage layer. Repositories must always + * attempt to match property types to the best matching native type, but they + * may have to rely on an adapter to make a conversion. + * + *

The annotation is just a pointer to an adapter implementation class. If + * the adapter class is not explicitly provided, it defaults to a static inner + * class named "Adapter" in the annotation itself. + * + *

The adapter class must have a public constructor that accepts the + * annotation that has the AdapterDefinition annotation. It must also define + * several adapt methods which convert property values. An adapt method needs + * to start with "adapt", accept one parameter and return something. + *

+ * Example true/false adapter for booleans: + *

+ * @Documented
+ * @Retention(RetentionPolicy.RUNTIME)
+ * @Target(ElementType.METHOD)
+ * @AdapterDefinition
+ * public @interface TrueFalseAdapter {
+ *
+ *     public static class Adapter {
+ *         private final String propertyName;
+ *
+ *         // Constructor may throw a MalformedTypeException if
+ *         // params supplied by annotation are illegal.
+ *
+ *         /**
+ *          * @param type optional type of object that contains the adapted property
+ *          * @param propertyName name of property with adapter
+ *          * @param annotation specific annotation that binds to this adapter class
+ *          */
+ *         public Adapter(Class type, String propertyName, TrueFalseAdapter annotation) {
+ *             this.propertyName = propertyName;
+ *         }
+ *
+ *         // Define at least two adapt methods for each supported property type.
+ *
+ *         /**
+ *          * @param propertyValue value to convert from
+ *          */
+ *         public char adaptToChar(boolean propertyValue) {
+ *             return value ? 'T' : 'F';
+ *         }
+ *
+ *         /**
+ *          * @param propertyValue value to convert from
+ *          */
+ *         public boolean adaptToBoolean(char propertyValue) {
+ *             if (propertyValue == 'T') { return true; };
+ *             if (propertyValue == 'F') { return false; };
+ *             throw new IllegalArgumentException
+ *                 ("Cannot adapt '" + value + "' into boolean for property \"" +
+ *                   mPropertyName + '"');
+ *         }
+ *     }
+ * }
+ * 
+ * + * The newly defined adapter can be applied to property accessors. + * + *
+ * public interface UserInfo extends Storable {
+ *     @TrueFalseAdapter
+ *     boolean isAdministrator();
+ * }
+ * 
+ * + * @author Brian S O'Neill + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.ANNOTATION_TYPE) +public @interface AdapterDefinition { + /** + * Specify class which will perform property adaptation. Must have a public + * constructor with the signature + * (Class type, String propertyName, Annotation), + * where Annotation refers to the annotation with the + * adapter definition. + * + *

The implementation class need not be explicitly specified. By + * default, the adapter class must be a static inner class of the + * annotation, named "Adapter". + */ + // Use void.class to mean default. + Class implementation() default void.class; + + /** + * Optionally specify the set of preferred storage types for storing the + * adapted property, in order of most preferred to least preferred. A type + * in the set must be supported by the adapt methods to be considered. + * + *

If the repository is independent, it needs help on deciding exactly + * how to store the adapted property. A dependent repository will not have + * as much flexibility in selecting an appropriate type, but it may still + * need a hint. + */ + Class[] storageTypePreferences() default {}; +} diff --git a/src/main/java/com/amazon/carbonado/adapter/DateTimeAdapter.java b/src/main/java/com/amazon/carbonado/adapter/DateTimeAdapter.java new file mode 100644 index 0000000..541e65b --- /dev/null +++ b/src/main/java/com/amazon/carbonado/adapter/DateTimeAdapter.java @@ -0,0 +1,220 @@ +/* + * Copyright 2006 Amazon Technologies, Inc. or its affiliates. + * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks + * of Amazon Technologies, Inc. or its affiliates. All rights reserved. + * + * 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.amazon.carbonado.adapter; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.sql.Time; +import java.sql.Timestamp; +import java.util.Date; + +import org.joda.time.DateMidnight; +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; +import org.joda.time.ReadableInstant; + +import com.amazon.carbonado.adapter.AdapterDefinition; + +/** + * Converts Joda-Time datetime objects to and from other forms. This adapter is + * applied automatically for all storable properties of type {@link DateTime} + * or {@link DateMidnight}. Explicit use allows a different time zone to be + * used. DateTimeAdapter can also be used to support {@link Date} properties, + * but it must be explicitly specified. + * + *

Example:

+ * public interface UserInfo extends Storable {
+ *     @DateTimeAdapter(timeZone="UTC")
+ *     DateTime getModifyDateTime();
+ *
+ *     ...
+ * }
+ * 
+ * + * @author Brian S O'Neill + * @author Justin Rudd + * @see AdapterDefinition + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@AdapterDefinition(storageTypePreferences={ + long.class, + Long.class, + String.class +}) +public @interface DateTimeAdapter { + /** + * Optionally specify a time zone to apply to new DateTimes, overriding the + * default time zone. + */ + String timeZone() default ""; + + /** + * Adapter implementation for {@link DateTimeAdapter}. + */ + public static class Adapter { + private static DateTimeZone toDateTimeZone(DateTimeAdapter ann) { + String id; + if (ann == null || (id = ann.timeZone()) == null || id.length() == 0) { + return null; + } + return DateTimeZone.forID(id); + } + + private final String mPropertyName; + private final DateTimeZone mZone; + + /** + * @param type type of object that contains the adapted property + * @param propertyName name of property with adapter + * @param ann specific annotation that binds to this adapter class + */ + public Adapter(Class type, String propertyName, DateTimeAdapter ann) { + this(type, propertyName, toDateTimeZone(ann)); + } + + /** + * @param type type of object that contains the adapted property + * @param propertyName name of property with + * @param zone time zone to apply, or null to use default + */ + public Adapter(Class type, String propertyName, DateTimeZone zone) { + mPropertyName = propertyName; + mZone = zone; + } + + // Adapt to DateTime... + + public DateTime adaptToDateTime(long instant) { + return new DateTime(instant, mZone); + } + + public DateTime adaptToDateTime(Long instant) { + return instant == null ? null : new DateTime(instant, mZone); + } + + public DateTime adaptToDateTime(String isoDateString) { + return isoDateString == null ? null : new DateTime(isoDateString, mZone); + } + + public DateTime adaptToDateTime(Date date) { + return date == null ? null : new DateTime(date, mZone); + } + + public DateMidnight adaptToDateMidnight(long instant) { + return new DateMidnight(instant, mZone); + } + + public DateMidnight adaptToDateMidnight(Long instant) { + return instant == null ? null : new DateMidnight(instant, mZone); + } + + public DateMidnight adaptToDateMidnight(String isoDateString) { + return isoDateString == null ? null : new DateMidnight(isoDateString, mZone); + } + + public DateMidnight adaptToDateMidnight(Date date) { + return date == null ? null : new DateMidnight(date, mZone); + } + + // Adapt from DateTime... (accept the more generic ReadableInstant) + + public long adaptToLong(ReadableInstant instant) { + if (instant != null) { + return instant.getMillis(); + } + throw new IllegalArgumentException + ("Cannot adapt null DateTime into long for property \"" + + mPropertyName + '"'); + } + + public Long adaptToLongObj(ReadableInstant instant) { + return instant == null ? null : instant.getMillis(); + } + + public String adaptToString(ReadableInstant instant) { + return instant == null ? null : instant.toString(); + } + + public Date adaptToDate(ReadableInstant instant) { + return instant == null ? null : new Date(instant.getMillis()); + } + + public java.sql.Date adaptToSqlDate(ReadableInstant instant) { + return instant == null ? null : new java.sql.Date(instant.getMillis()); + } + + public Time adaptToSqlTime(ReadableInstant instant) { + return instant == null ? null : new Time(instant.getMillis()); + } + + public Timestamp adaptToSqlTimestamp(ReadableInstant instant) { + return instant == null ? null : new Timestamp(instant.getMillis()); + } + + // Adapt to Date... + + public Date adaptToDate(long instant) { + return new Date(instant); + } + + public Date adaptToDate(Long instant) { + return instant == null ? null : new Date(instant); + } + + public Date adaptToDate(String isoDateString) { + return isoDateString == null ? null : new DateTime(isoDateString, mZone).toDate(); + } + + // Adapt from Date... + + public long adaptToLong(Date date) { + if (date != null) { + return date.getTime(); + } + throw new IllegalArgumentException + ("Cannot adapt null Date into long for property \"" + + mPropertyName + '"'); + } + + public Long adaptToLongObj(Date date) { + return date == null ? null : date.getTime(); + } + + public String adaptToString(Date date) { + return date == null ? null : new DateTime(date, mZone).toString(); + } + + public java.sql.Date adaptToSqlDate(Date date) { + return date == null ? null : new java.sql.Date(date.getTime()); + } + + public Time adaptToSqlTime(Date date) { + return date == null ? null : new Time(date.getTime()); + } + + public Timestamp adaptToSqlTimestamp(Date date) { + return date == null ? null : new Timestamp(date.getTime()); + } + } +} diff --git a/src/main/java/com/amazon/carbonado/adapter/TextAdapter.java b/src/main/java/com/amazon/carbonado/adapter/TextAdapter.java new file mode 100644 index 0000000..e07ed50 --- /dev/null +++ b/src/main/java/com/amazon/carbonado/adapter/TextAdapter.java @@ -0,0 +1,244 @@ +/* + * Copyright 2006 Amazon Technologies, Inc. or its affiliates. + * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks + * of Amazon Technologies, Inc. or its affiliates. All rights reserved. + * + * 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.amazon.carbonado.adapter; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Writer; + +import java.nio.charset.CharacterCodingException; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; + +import com.amazon.carbonado.FetchException; +import com.amazon.carbonado.PersistException; + +import com.amazon.carbonado.lob.Blob; +import com.amazon.carbonado.lob.ByteArrayBlob; +import com.amazon.carbonado.lob.Clob; +import com.amazon.carbonado.lob.StringClob; + +import com.amazon.carbonado.adapter.AdapterDefinition; + +/** + * Converts database Blobs and Clobs to Strings. This is suitable for text + * values which are expected to fit entirely in memory. The storage layer will + * attempt to store the text as a regular string, but will use a blob type if + * required to. + * + *

Example:

+ * public interface UserInfo extends Storable {
+ *     @TextAdapter(charset="UTF-8")
+ *     String getWelcomeMessage();
+ *
+ *     ...
+ * }
+ * 
+ * + * @author Brian S O'Neill + * @see Clob + * @see Blob + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@AdapterDefinition(storageTypePreferences={ + String.class, + Clob.class, + Blob.class +}) +public @interface TextAdapter { + /** + * Optionally specify a character set, which is used if the storage type is + * a Blob. + */ + String charset() default "UTF-8"; + + /** + * Optionally specify alternate character sets, which are used if text + * cannot be decoded with primary charset. + */ + String[] altCharsets() default {}; + + /** + * Adapter implementation for {@link TextAdapter}. + */ + public static class Adapter { + private static byte[] EMPTY_BYTE_ARRAY = new byte[0]; + + static PersistException toPersistException(IOException e) { + Throwable cause = e.getCause(); + if (cause instanceof PersistException) { + return (PersistException) cause; + } + if (cause == null) { + cause = e; + } + return new PersistException(cause); + } + + private static final Charset[] prepareAltCharsets(TextAdapter ann) { + if (ann == null) { + return null; + } + String[] strs = ann.altCharsets(); + if (strs == null || strs.length == 0) { + return null; + } + Charset[] altCharsets = new Charset[strs.length]; + for (int i=0; i type, String propertyName, TextAdapter ann) { + this(type, propertyName, + (ann == null ? null : Charset.forName(ann.charset())), + prepareAltCharsets(ann)); + } + + /** + * @param type type of object that contains the adapted property + * @param propertyName name of property with + * @param charset character set to use, or null to use default of UTF-8. + */ + public Adapter(Class type, String propertyName, String charset) { + this(type, propertyName, charset == null ? null : Charset.forName(charset), null); + } + + /** + * @param type type of object that contains the adapted property + * @param propertyName name of property with + * @param charset character set to use, or null to use default of UTF-8. + */ + public Adapter(Class type, String propertyName, Charset charset) { + this(type, propertyName, charset, null); + } + + /** + * @param type type of object that contains the adapted property + * @param propertyName name of property with + * @param charset character set to use, or null to use default of UTF-8. + * @param altCharsets alternate character sets to use, if text cannot be + * decoded with primary charset + */ + public Adapter(Class type, String propertyName, Charset charset, Charset[] altCharsets) + { + mCharset = charset == null ? Charset.forName("UTF-8") : charset; + if (altCharsets == null || altCharsets.length == 0) { + altCharsets = null; + } else { + altCharsets = altCharsets.clone(); + } + mAltCharsets = altCharsets; + } + + // Adapt to String... + + public String adaptToString(Clob clob) throws FetchException { + return clob == null ? null : clob.asString(); + } + + public String adaptToString(Blob blob) throws FetchException { + FetchException error; + + try { + return blob == null ? null : blob.asString(mCharset); + } catch (FetchException e) { + if (mAltCharsets == null) { + throw e; + } + Throwable cause = e.getCause(); + if (!(cause instanceof CharacterCodingException)) { + throw e; + } + error = e; + } + + for (int i=0; i= Integer.MAX_VALUE) { + byteLength = Integer.MAX_VALUE; + } + + byte[] buffer = new byte[(int) byteLength]; + + Blob blob = new ByteArrayBlob(buffer, 0); + + Writer w = new OutputStreamWriter(blob.openOutputStream(), encoder); + try { + w.write(text, 0, text.length()); + w.close(); + } catch (IOException e) { + throw toPersistException(e); + } + + return blob; + } + } +} diff --git a/src/main/java/com/amazon/carbonado/adapter/TrueFalseAdapter.java b/src/main/java/com/amazon/carbonado/adapter/TrueFalseAdapter.java new file mode 100644 index 0000000..886b994 --- /dev/null +++ b/src/main/java/com/amazon/carbonado/adapter/TrueFalseAdapter.java @@ -0,0 +1,226 @@ +/* + * Copyright 2006 Amazon Technologies, Inc. or its affiliates. + * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks + * of Amazon Technologies, Inc. or its affiliates. All rights reserved. + * + * 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.amazon.carbonado.adapter; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Adapter that converts 'T' or 'F' to and from a boolean value. + * + *

Example:

+ * public interface UserInfo extends Storable {
+ *     @TrueFalseAdapter
+ *     boolean isAdministrator();
+ *
+ *     void setAdministrator(boolean admin);
+ *
+ *     ...
+ * }
+ * 
+ * + * @author Brian S O'Neill + * @see YesNoAdapter + * @see AdapterDefinition + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@AdapterDefinition(storageTypePreferences={ + char.class, + Character.class, + String.class +}) +public @interface TrueFalseAdapter { + /** + * Specify that this adapter should be lenient when converting characters + * into booleans. By default it is true, and it accepts the following as + * true: 'T', 't', 'Y', 'y', '1'. For false: 'F', 'f', 'N', 'n', '0'. When + * lenient is false, only 'T' and 'F' are accepted. + */ + boolean lenient() default true; + + /** + * Adapter implementation for {@link TrueFalseAdapter}. + */ + public static class Adapter { + private final String mPropertyName; + private final boolean mLenient; + + /** + * @param type type of object that contains the adapted property + * @param propertyName name of property with adapter + * @param ann specific annotation that binds to this adapter class + */ + public Adapter(Class type, String propertyName, TrueFalseAdapter ann) { + this(type, propertyName, ann == null ? true : ann.lenient()); + } + + /** + * @param type type of object that contains the adapted property + * @param propertyName name of property with + * @param lenient lenient when true + */ + public Adapter(Class type, String propertyName, boolean lenient) { + mPropertyName = propertyName; + mLenient = lenient; + } + + /** + * Adapts a boolean true or false into 'T' or 'F'. + */ + public char adaptToChar(boolean value) { + return value ? 'T' : 'F'; + } + + /** + * Adapts a boolean true into 'T', and anything else to 'F'. + */ + public char adaptToChar(Boolean value) { + return (value != null && value) ? 'T' : 'F'; + } + + /** + * Adapts a boolean true or false into 'T' or 'F'. + */ + public Character adaptToCharacter(boolean value) { + return value ? 'T' : 'F'; + } + + /** + * Adapts a boolean true into 'T', and anything else to 'F'. + */ + public Character adaptToCharacter(Boolean value) { + return (value != null && value) ? 'T' : 'F'; + } + + /** + * Adapts a boolean true or false into "T" or "F". + */ + public String adaptToString(boolean value) { + return value ? "T" : "F"; + } + + /** + * Adapts a boolean true into "T", and anything else to "F". + */ + public String adaptToString(Boolean value) { + return (value != null && value) ? "T" : "F"; + } + + /** + * Adapts a character 'T' or 'F' to true or false. If {@link + * TrueFalseAdapter#lenient lenient}, other characters are accepted as + * well. + */ + public boolean adaptToBoolean(char value) { + switch (value) { + case 'T': + return true; + case 'F': + return false; + + case 't': case 'Y': case 'y': case '1': + if (mLenient) { + return true; + } + break; + + case 'f': case 'N': case 'n': case '0': + if (mLenient) { + return false; + } + break; + } + + throw new IllegalArgumentException + ("Cannot adapt '" + value + "' into boolean for property \"" + + mPropertyName + '"'); + } + + /** + * Adapts a character 'T' or 'F' to true or false. If {@link + * TrueFalseAdapter#lenient lenient}, other characters are accepted as + * well. + */ + public boolean adaptToBoolean(Character value) { + if (value == null) { + if (mLenient) { + return false; + } + } else { + return adaptToBoolean(value.charValue()); + } + + throw new IllegalArgumentException + ("Cannot adapt '" + value + "' into boolean for property \"" + + mPropertyName + '"'); + } + + /** + * Adapts a character "T" or "F" to true or false. If {@link + * TrueFalseAdapter#lenient leneint}, other characters are accepted as + * well. + */ + public boolean adaptToBoolean(String value) { + int length; + if (value == null || (length = value.length()) == 0) { + if (mLenient) { + return false; + } + } else if (length == 1 || mLenient) { + return adaptToBoolean(value.charAt(0)); + } + + throw new IllegalArgumentException + ("Cannot adapt '" + value + "' into boolean for property \"" + + mPropertyName + '"'); + } + + /** + * Adapts a character 'T' or 'F' to true or false. If {@link + * TrueFalseAdapter#lenient lenient}, other characters are accepted as + * well. + */ + public Boolean adaptToBooleanObj(char value) { + return adaptToBoolean(value); + } + + /** + * Adapts a character 'T' or 'F' to true or false. If {@link + * TrueFalseAdapter#lenient lenient}, other characters are accepted as + * well. + */ + public Boolean adaptToBooleanObj(Character value) { + return adaptToBoolean(value); + } + + /** + * Adapts a character "T" or "F" to true or false. If {@link + * TrueFalseAdapter#lenient lenient}, other characters are accepted as + * well. + */ + public Boolean adaptToBooleanObj(String value) { + return adaptToBoolean(value); + } + } +} diff --git a/src/main/java/com/amazon/carbonado/adapter/YesNoAdapter.java b/src/main/java/com/amazon/carbonado/adapter/YesNoAdapter.java new file mode 100644 index 0000000..4d9c979 --- /dev/null +++ b/src/main/java/com/amazon/carbonado/adapter/YesNoAdapter.java @@ -0,0 +1,226 @@ +/* + * Copyright 2006 Amazon Technologies, Inc. or its affiliates. + * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks + * of Amazon Technologies, Inc. or its affiliates. All rights reserved. + * + * 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.amazon.carbonado.adapter; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Adapter that converts 'Y' or 'N' to and from a boolean value. + * + *

Example:

+ * public interface UserInfo extends Storable {
+ *     @YesNoAdapter
+ *     boolean isAdministrator();
+ *
+ *     void setAdministrator(boolean admin);
+ *
+ *     ...
+ * }
+ * 
+ * + * @author Brian S O'Neill + * @see TrueFalseAdapter + * @see AdapterDefinition + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@AdapterDefinition(storageTypePreferences={ + char.class, + Character.class, + String.class +}) +public @interface YesNoAdapter { + /** + * Specify that this adapter should be lenient when converting characters + * into booleans. By default it is true, and it accepts the following as + * true: 'Y', 'y', 'T', 't', '1'. For false: 'N', 'n', 'F', 'f', '0'. When + * lenient is false, only 'Y' and 'N' are accepted. + */ + boolean lenient() default true; + + /** + * Adapter implementation for {@link YesNoAdapter}. + */ + public static class Adapter { + private final String mPropertyName; + private final boolean mLenient; + + /** + * @param type type of object that contains the adapted property + * @param propertyName name of property with adapter + * @param ann specific annotation that binds to this adapter class + */ + public Adapter(Class type, String propertyName, YesNoAdapter ann) { + this(type, propertyName, ann == null ? true : ann.lenient()); + } + + /** + * @param type type of object that contains the adapted property + * @param propertyName name of property with + * @param lenient lenient when true + */ + public Adapter(Class type, String propertyName, boolean lenient) { + mPropertyName = propertyName; + mLenient = lenient; + } + + /** + * Adapts a boolean true or false into 'Y' or 'N'. + */ + public char adaptToChar(boolean value) { + return value ? 'Y' : 'N'; + } + + /** + * Adapts a boolean true into 'Y', and anything else to 'N'. + */ + public char adaptToChar(Boolean value) { + return (value != null && value) ? 'Y' : 'N'; + } + + /** + * Adapts a boolean true or false into 'Y' or 'N'. + */ + public Character adaptToCharacter(boolean value) { + return value ? 'Y' : 'N'; + } + + /** + * Adapts a boolean true into 'Y', and anything else to 'N'. + */ + public Character adaptToCharacter(Boolean value) { + return (value != null && value) ? 'Y' : 'N'; + } + + /** + * Adapts a boolean true or false into "Y" or "N". + */ + public String adaptToString(boolean value) { + return value ? "Y" : "N"; + } + + /** + * Adapts a boolean true into "Y", and anything else to "N". + */ + public String adaptToString(Boolean value) { + return (value != null && value) ? "Y" : "N"; + } + + /** + * Adapts a character 'Y' or 'N' to true or false. If {@link + * YesNoAdapter#lenient lenient}, other characters are accepted as + * well. + */ + public boolean adaptToBoolean(char value) { + switch (value) { + case 'Y': + return true; + case 'N': + return false; + + case 'y': case 'T': case 't': case '1': + if (mLenient) { + return true; + } + break; + + case 'n': case 'F': case 'f': case '0': + if (mLenient) { + return false; + } + break; + } + + throw new IllegalArgumentException + ("Cannot adapt '" + value + "' into boolean for property \"" + + mPropertyName + '"'); + } + + /** + * Adapts a character 'Y' or 'N' to true or false. If {@link + * YesNoAdapter#lenient lenient}, other characters are accepted as + * well. + */ + public boolean adaptToBoolean(Character value) { + if (value == null) { + if (mLenient) { + return false; + } + } else { + return adaptToBoolean(value.charValue()); + } + + throw new IllegalArgumentException + ("Cannot adapt '" + value + "' into boolean for property \"" + + mPropertyName + '"'); + } + + /** + * Adapts a character "Y" or "N" to true or false. If {@link + * YesNoAdapter#lenient lenient}, other characters are accepted as + * well. + */ + public boolean adaptToBoolean(String value) { + int length; + if (value == null || (length = value.length()) == 0) { + if (mLenient) { + return false; + } + } else if (length == 1 || mLenient) { + return adaptToBoolean(value.charAt(0)); + } + + throw new IllegalArgumentException + ("Cannot adapt '" + value + "' into boolean for property \"" + + mPropertyName + '"'); + } + + /** + * Adapts a character 'Y' or 'N' to true or false. If {@link + * YesNoAdapter#lenient lenient}, other characters are accepted as + * well. + */ + public Boolean adaptToBooleanObj(char value) { + return adaptToBoolean(value); + } + + /** + * Adapts a character 'Y' or 'N' to true or false. If {@link + * YesNoAdapter#lenient lenient}, other characters are accepted as + * well. + */ + public Boolean adaptToBooleanObj(Character value) { + return adaptToBoolean(value); + } + + /** + * Adapts a character "Y" or "N" to true or false. If {@link + * YesNoAdapter#lenient lenient}, other characters are accepted as + * well. + */ + public Boolean adaptToBooleanObj(String value) { + return adaptToBoolean(value); + } + } +} diff --git a/src/main/java/com/amazon/carbonado/adapter/package-info.java b/src/main/java/com/amazon/carbonado/adapter/package-info.java new file mode 100644 index 0000000..7c8f8b1 --- /dev/null +++ b/src/main/java/com/amazon/carbonado/adapter/package-info.java @@ -0,0 +1,24 @@ +/* + * Copyright 2006 Amazon Technologies, Inc. or its affiliates. + * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks + * of Amazon Technologies, Inc. or its affiliates. All rights reserved. + * + * 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. + */ + +/** + * Contains annotations and implementations for supporting property adapters. + * + * @see com.amazon.carbonado.adapter.AdapterDefinition + */ +package com.amazon.carbonado.adapter; -- cgit v1.2.3