001/* 002 * ModeShape (http://www.modeshape.org) 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.modeshape.jdbc; 017 018import java.io.ByteArrayInputStream; 019import java.io.IOException; 020import java.io.InputStream; 021import java.math.BigDecimal; 022import java.text.ParseException; 023import java.text.SimpleDateFormat; 024import java.util.Calendar; 025import java.util.Date; 026import javax.jcr.Binary; 027import javax.jcr.PropertyType; 028import javax.jcr.RepositoryException; 029import javax.jcr.Value; 030import javax.jcr.ValueFormatException; 031 032/** 033 * A factory class which creates {@link javax.jcr.Value} instances from arbitrary objects (avoiding the dependency on the real 034 * {@link javax.jcr.ValueFactory} implementations from other modules). 035 * 036 * @author Horia Chiorean 037 */ 038public final class JdbcJcrValueFactory { 039 040 protected static final SimpleDateFormat ISO8601 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); 041 042 private JdbcJcrValueFactory() { 043 } 044 045 /** 046 * Creates a new {@link javax.jcr.Value} instance to be used by the JDBC driver. 047 * 048 * @param value an actual value which will be wrapped by the JCR value; may be null 049 * @return either a {@link javax.jcr.Value} instance or {@code null} if the given argument is {@code null} or is the string 050 * {@code NULL} 051 */ 052 public static Value createValue( Object value ) { 053 if (value == null) { 054 return null; 055 } 056 if (value instanceof String && ((String)value).equalsIgnoreCase("NULL")) { 057 return null; 058 } 059 return new JdbcJcrValue(value); 060 } 061 062 private static class JdbcJcrValue implements Value { 063 064 private final Object value; 065 066 protected JdbcJcrValue( Object value ) { 067 assert value != null; 068 this.value = value; 069 } 070 071 @Override 072 public boolean getBoolean() throws IllegalStateException { 073 if (value instanceof Boolean) { 074 return (Boolean)value; 075 } 076 return Boolean.parseBoolean(value.toString()); 077 } 078 079 @Override 080 public Calendar getDate() throws ValueFormatException, IllegalStateException, RepositoryException { 081 if (value instanceof Date) { 082 Calendar c = Calendar.getInstance(); 083 c.setTime((Date)value); 084 return c; 085 } else if (value instanceof Calendar) { 086 return (Calendar)value; 087 } 088 089 try { 090 Date iso8601Format = ISO8601.parse(value.toString()); 091 Calendar c = Calendar.getInstance(); 092 c.setTime(iso8601Format); 093 return c; 094 } catch (ParseException e) { 095 throw new ValueFormatException("Value not instance of Date", e); 096 } 097 } 098 099 @Override 100 public double getDouble() throws ValueFormatException, IllegalStateException, RepositoryException { 101 if (value instanceof Number) { 102 return ((Number)value).doubleValue(); 103 } 104 105 try { 106 return Double.parseDouble(value.toString()); 107 } catch (NumberFormatException e) { 108 throw new ValueFormatException("Value not a Double", e); 109 } 110 } 111 112 @Override 113 public long getLong() throws ValueFormatException, IllegalStateException, RepositoryException { 114 if (value instanceof Number) { 115 return ((Number)value).longValue(); 116 } 117 118 try { 119 return Long.parseLong(value.toString()); 120 } catch (NumberFormatException e) { 121 throw new ValueFormatException("Value not a Long"); 122 } 123 } 124 125 @Override 126 public Binary getBinary() throws RepositoryException { 127 if (value instanceof Binary) { 128 return ((Binary)value); 129 } 130 if (value instanceof byte[]) { 131 final byte[] bytes = (byte[])value; 132 return new Binary() { 133 @Override 134 public void dispose() { 135 } 136 137 @Override 138 public long getSize() { 139 return bytes.length; 140 } 141 142 @Override 143 public InputStream getStream() { 144 return new ByteArrayInputStream(bytes); 145 } 146 147 @Override 148 public int read( byte[] b, 149 long position ) throws IOException { 150 if (getSize() <= position) { 151 return -1; 152 } 153 InputStream stream = null; 154 try { 155 stream = getStream(); 156 // Read/skip the next 'position' bytes ... 157 long skip = position; 158 while (skip > 0) { 159 long skipped = stream.skip(skip); 160 if (skipped <= 0) { 161 return -1; 162 } 163 skip -= skipped; 164 } 165 return stream.read(b); 166 } finally { 167 if (stream != null) { 168 try { 169 stream.close(); 170 } catch (Exception e) { 171 // ignore 172 } 173 } 174 } 175 } 176 177 }; 178 } 179 throw new ValueFormatException("Value not a Binary"); 180 } 181 182 @Override 183 public BigDecimal getDecimal() throws ValueFormatException, RepositoryException { 184 if (value instanceof BigDecimal) { 185 return ((BigDecimal)value); 186 } 187 try { 188 return new BigDecimal(value.toString()); 189 } catch (NumberFormatException e) { 190 throw new ValueFormatException("Value not a Decimal"); 191 } 192 } 193 194 @Override 195 public InputStream getStream() throws IllegalStateException, RepositoryException { 196 if (value instanceof Binary) { 197 return ((Binary)value).getStream(); 198 } 199 if (value instanceof InputStream) { 200 return ((InputStream)value); 201 } 202 if (value instanceof byte[]) { 203 return new ByteArrayInputStream((byte[])value); 204 } 205 throw new ValueFormatException("Value not an InputStream"); 206 } 207 208 @Override 209 public String getString() throws IllegalStateException { 210 if (value instanceof String) { 211 return (String)value; 212 } 213 return value.toString(); 214 } 215 216 @Override 217 public int getType() { 218 if (value instanceof String) { 219 return PropertyType.STRING; 220 } 221 if (value instanceof Boolean) { 222 return PropertyType.BOOLEAN; 223 } 224 if (value instanceof Date || value instanceof Calendar) { 225 return PropertyType.DATE; 226 } 227 if (value instanceof Double || value instanceof Float) { 228 return PropertyType.DOUBLE; 229 } 230 if (value instanceof Long || value instanceof Integer) { 231 return PropertyType.LONG; 232 } 233 if (value instanceof BigDecimal) { 234 return PropertyType.DECIMAL; 235 } 236 if (value instanceof byte[] || value instanceof Binary || value instanceof InputStream) { 237 return PropertyType.BINARY; 238 } 239 return PropertyType.UNDEFINED; 240 } 241 } 242 243}