summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main/java/com/amazon/carbonado/adapter/AdapterDefinition.java124
-rw-r--r--src/main/java/com/amazon/carbonado/adapter/DateTimeAdapter.java220
-rw-r--r--src/main/java/com/amazon/carbonado/adapter/TextAdapter.java244
-rw-r--r--src/main/java/com/amazon/carbonado/adapter/TrueFalseAdapter.java226
-rw-r--r--src/main/java/com/amazon/carbonado/adapter/YesNoAdapter.java226
-rw-r--r--src/main/java/com/amazon/carbonado/adapter/package-info.java24
6 files changed, 1064 insertions, 0 deletions
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.
+ *
+ * <p>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.
+ *
+ * <p>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.
+ * <p>
+ * Example true/false adapter for booleans:
+ * <pre>
+ * &#64;Documented
+ * <b>&#64;Retention(RetentionPolicy.RUNTIME)</b>
+ * <b>&#64;Target(ElementType.METHOD)</b>
+ * <b>&#64;AdapterDefinition</b>
+ * public &#64;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
+ * *&#47;
+ * 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
+ * *&#47;
+ * public char adaptToChar(boolean propertyValue) {
+ * return value ? 'T' : 'F';
+ * }
+ *
+ * /**
+ * * @param propertyValue value to convert from
+ * *&#47;
+ * 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 + '"');
+ * }
+ * }
+ * }
+ * </pre>
+ *
+ * The newly defined adapter can be applied to property accessors.
+ *
+ * <pre>
+ * public interface UserInfo extends Storable {
+ * <b>&#64;TrueFalseAdapter</b>
+ * boolean isAdministrator();
+ * }
+ * </pre>
+ *
+ * @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
+ * <code>(Class type, String propertyName, <i>Annotation</i>)</code>,
+ * where <code><i>Annotation</i></code> refers to the annotation with the
+ * adapter definition.
+ *
+ * <p>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.
+ *
+ * <p>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.
+ *
+ * <p>Example:<pre>
+ * public interface UserInfo extends Storable {
+ * <b>&#64;DateTimeAdapter(timeZone="UTC")</b>
+ * DateTime getModifyDateTime();
+ *
+ * ...
+ * }
+ * </pre>
+ *
+ * @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.
+ *
+ * <p>Example:<pre>
+ * public interface UserInfo extends Storable {
+ * <b>&#64;TextAdapter(charset="UTF-8")</b>
+ * String getWelcomeMessage();
+ *
+ * ...
+ * }
+ * </pre>
+ *
+ * @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<strs.length; i++) {
+ altCharsets[i] = Charset.forName(strs[i]);
+ }
+ return altCharsets;
+ }
+
+ private final Charset mCharset;
+ private final Charset[] mAltCharsets;
+
+ /**
+ * @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, 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<mAltCharsets.length; i++) {
+ try {
+ return blob.asString(mAltCharsets[i]);
+ } catch (FetchException e) {
+ Throwable cause = e.getCause();
+ if (!(cause instanceof CharacterCodingException)) {
+ throw e;
+ }
+ }
+ }
+
+ throw error;
+ }
+
+ // Adapt from String...
+
+ public Clob adaptToClob(String text) {
+ if (text == null) {
+ return null;
+ }
+ return new StringClob(text);
+ }
+
+ public Blob adaptToBlob(String text) throws PersistException {
+ if (text == null) {
+ return null;
+ }
+
+ if (text.length() == 0) {
+ return new ByteArrayBlob(EMPTY_BYTE_ARRAY);
+ }
+
+ CharsetEncoder encoder = mCharset.newEncoder();
+
+ long byteLength = (long) (text.length() * encoder.averageBytesPerChar());
+
+ if (byteLength >= 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.
+ *
+ * <p>Example:<pre>
+ * public interface UserInfo extends Storable {
+ * <b>&#64;TrueFalseAdapter</b>
+ * boolean isAdministrator();
+ *
+ * void setAdministrator(boolean admin);
+ *
+ * ...
+ * }
+ * </pre>
+ *
+ * @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.
+ *
+ * <p>Example:<pre>
+ * public interface UserInfo extends Storable {
+ * <b>&#64;YesNoAdapter</b>
+ * boolean isAdministrator();
+ *
+ * void setAdministrator(boolean admin);
+ *
+ * ...
+ * }
+ * </pre>
+ *
+ * @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;