/* * Copyright 2013 cruxframework.org. * * 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 org.cruxframework.crux.core.rebind.database.idb; import java.io.PrintWriter; import java.util.HashSet; import java.util.Set; import org.cruxframework.crux.core.client.collection.FastList; import org.cruxframework.crux.core.client.db.DatabaseErrorHandler; import org.cruxframework.crux.core.client.db.IDXAbstractDatabase; import org.cruxframework.crux.core.client.db.IDXFileStore; import org.cruxframework.crux.core.client.db.ObjectStore; import org.cruxframework.crux.core.client.db.annotation.DatabaseDef.IndexDef; import org.cruxframework.crux.core.client.db.annotation.DatabaseDef.ObjectStoreDef; import org.cruxframework.crux.core.client.db.indexeddb.IDBIndexParameters; import org.cruxframework.crux.core.client.db.indexeddb.IDBObjectStore; import org.cruxframework.crux.core.client.db.indexeddb.IDBObjectStoreParameters; import org.cruxframework.crux.core.client.db.indexeddb.IDBOpenDBRequest; import org.cruxframework.crux.core.client.utils.EscapeUtils; import org.cruxframework.crux.core.client.utils.StringUtils; import org.cruxframework.crux.core.rebind.CruxGeneratorException; import org.cruxframework.crux.core.rebind.context.RebindContext; import org.cruxframework.crux.core.rebind.database.AbstractDatabaseProxyCreator; import com.google.gwt.core.client.GWT; import com.google.gwt.core.ext.typeinfo.JClassType; import com.google.gwt.core.ext.typeinfo.JPackage; import com.google.gwt.user.rebind.ClassSourceFileComposerFactory; /** * This class creates a client proxy for access a database * * @author Thiago da Rosa de Bustamante * */ public class IDBDatabaseProxyCreator extends AbstractDatabaseProxyCreator { public IDBDatabaseProxyCreator(RebindContext context, JClassType baseIntf) { super(context, baseIntf); } @Override protected void generateProxyMethods(SourcePrinter srcWriter) { generateUpdateDatabaseStructureMethod(srcWriter); generateGetObjectStoreMethod(srcWriter); } protected void generateUpdateDatabaseStructureMethod(SourcePrinter srcWriter) { srcWriter.println("protected void updateDatabaseStructure(IDBOpenDBRequest openDBRequest){"); generateObjectStoresCreation(srcWriter, "openDBRequest"); srcWriter.println("}"); srcWriter.println(); } protected void generateGetObjectStoreMethod(SourcePrinter srcWriter) { srcWriter.println("protected <K, V> ObjectStore<K, V> getObjectStore(String storeName, IDBObjectStore idbObjectStore){"); boolean first = true; ObjectStoreDef[] objectStores = databaseMetadata.objectStores(); for (ObjectStoreDef objectStoreMetadata : objectStores) { JClassType objectStoreTarget = getObjectStoreTarget(objectStoreMetadata); String objectStoreName = getObjectStoreName(objectStoreMetadata, objectStoreTarget); if (!first) { srcWriter.print("else "); } first = false; Set<IndexData> indexes = getIndexes(objectStoreMetadata.indexes(), objectStoreTarget, objectStoreName); srcWriter.println("if (StringUtils.unsafeEquals(storeName, "+EscapeUtils.quote(objectStoreName)+")){"); String[] keyPath = getKeyPath(objectStoreMetadata, objectStoreTarget); String objectStore = new IDBObjectStoreProxyCreator(context, objectStoreTarget, objectStoreName, keyPath, indexes).create(); srcWriter.println("return (ObjectStore<K, V>) new "+objectStore+"(this, idbObjectStore);"); srcWriter.println("}"); } srcWriter.println("return null;"); srcWriter.println("}"); srcWriter.println(); } protected void generateObjectStoresCreation(SourcePrinter srcWriter, String requestVar) { ObjectStoreDef[] objectStores = databaseMetadata.objectStores(); Set<String> addedObjectStores = new HashSet<String>(); String objectStoreVar = "objectStore"; String indexNamesVar = "indexNames"; srcWriter.println("IDBObjectStore "+objectStoreVar+";"); srcWriter.println(FastList.class.getCanonicalName() +"<String> storeNames = db.listObjectStoreNames();"); srcWriter.println("for(int i=0; i< storeNames.size(); i++){"); srcWriter.println("String storeName = storeNames.get(i);"); srcWriter.println("try{"); srcWriter.println("db.deleteObjectStore(storeName);"); srcWriter.println("}catch (Exception e){/* Chrome BUG. When an object store is created, but have no data, chrome raises a NotFoundException when removing these store. So ignore any delete failed attempt.*/}"); srcWriter.println("}"); for (ObjectStoreDef objectStoreMetadata : objectStores) { JClassType objectStoreTarget = getObjectStoreTarget(objectStoreMetadata); String objectStoreName = getObjectStoreName(objectStoreMetadata, objectStoreTarget); if (addedObjectStores.contains(objectStoreName)) { throw new CruxGeneratorException("Duplicated objectstore declared on Datasource ["+databaseMetadata.name()+"]"); } addedObjectStores.add(objectStoreName); if (objectStoreMetadata.keyPath().length == 1) { generateObjectStoreCreation(srcWriter, objectStoreMetadata.keyPath()[0], objectStoreMetadata.autoIncrement(), objectStoreName, objectStoreVar); } else if (objectStoreMetadata.keyPath().length > 1) { generateObjectStoreCreation(srcWriter, objectStoreMetadata.keyPath(), objectStoreMetadata.autoIncrement(), objectStoreName, objectStoreVar); } else if (objectStoreTarget != null && !objectStoreTarget.isAssignableTo(emptyType)) { String[] keyPath = getKeyPath(objectStoreTarget); if (keyPath == null || keyPath.length == 0) { throw new CruxGeneratorException("can not create an objectStore without a key definition. ObjectStore["+objectStoreName+"]."); } else if (keyPath.length == 1) { generateObjectStoreCreation(srcWriter, keyPath[0], isAutoIncrement(objectStoreTarget), objectStoreName, objectStoreVar); } else { generateObjectStoreCreation(srcWriter, keyPath, isAutoIncrement(objectStoreTarget), objectStoreName, objectStoreVar); } } else { throw new CruxGeneratorException("can not create an objectStore without a key definition. ObjectStore["+objectStoreName+"]."); } generateIndexesCreation(srcWriter, objectStoreMetadata.indexes(), objectStoreTarget, objectStoreVar, objectStoreName, indexNamesVar); } generateFileStoreCreation(srcWriter); } //TODO remover indices antigos...que foram removidos do metadado protected void generateIndexesCreation(SourcePrinter srcWriter, IndexDef[] indexMetadata, JClassType objectStoreTarget, String objectStoreVar, String objectStoreName, String indexNamesVar) { Set<IndexData> indexesCreated = getIndexes(indexMetadata, objectStoreTarget, objectStoreName); for (IndexData index : indexesCreated) { if (index.keyPath.length == 1) {//TODO ensureValidIdentifier... remove ',' etc generateIndexCreation(srcWriter, index.keyPath[0], index.unique, false, index.indexName, objectStoreVar, indexNamesVar); } else { generateIndexCreation(srcWriter, index.keyPath, index.unique, index.multiEntry, index.indexName, objectStoreVar, indexNamesVar); } } } protected void generateIndexCreation(SourcePrinter srcWriter, String keyPath, boolean unique, boolean multiEntry, String name, String objectStoreVar, String indexNamesVar) { srcWriter.println(objectStoreVar+".createIndex("+EscapeUtils.quote(name)+", "+ EscapeUtils.quote(keyPath) + ", " + IDBIndexParameters.class.getCanonicalName()+".create("+unique+", "+multiEntry+"));"); } protected void generateIndexCreation(SourcePrinter srcWriter, String[] keyPaths, boolean unique, boolean multiEntry, String name, String objectStoreVar, String indexNamesVar) { srcWriter.println(objectStoreVar+".createIndex("+EscapeUtils.quote(name)+", new String[]{"); boolean first = true; for (String keyPath : keyPaths) { if (!first) { srcWriter.print(", "); } first = false; srcWriter.print(EscapeUtils.quote(keyPath)); } srcWriter.println("}, " + IDBIndexParameters.class.getCanonicalName()+".create("+unique+", "+multiEntry+"));"); } protected void generateFileStoreCreation(SourcePrinter srcWriter) { srcWriter.println("db.createObjectStore("+EscapeUtils.quote(IDXFileStore.OBJECT_STORE_NAME)+");"); } protected void generateObjectStoreCreation(SourcePrinter srcWriter, String keyPath, boolean autoIncrement, String objectStoreName, String objectStoreVar) { srcWriter.println(objectStoreVar+" = db.createObjectStore("+EscapeUtils.quote(objectStoreName)+", "+ IDBObjectStoreParameters.class.getCanonicalName()+".create("+EscapeUtils.quote(keyPath)+", "+autoIncrement+"));"); } protected void generateObjectStoreCreation(SourcePrinter srcWriter, String[] keyPaths, boolean autoIncrement, String objectStoreName, String objectStoreVar) { srcWriter.println(objectStoreVar+" = db.createObjectStore("+EscapeUtils.quote(objectStoreName)+", "+ IDBObjectStoreParameters.class.getCanonicalName()+".create(new String[]{"); boolean first = true; for (String keyPath : keyPaths) { if (!first) { srcWriter.print(", "); } first = false; srcWriter.print(EscapeUtils.quote(keyPath)); } srcWriter.println("}, "+autoIncrement+"));"); } @Override protected String[] getImports() { return new String[]{ IDBObjectStore.class.getCanonicalName(), IDBOpenDBRequest.class.getCanonicalName(), DatabaseErrorHandler.class.getCanonicalName(), ObjectStore.class.getCanonicalName(), StringUtils.class.getCanonicalName(), GWT.class.getCanonicalName() }; } /** * @return the simple name of the proxy object. */ @Override public String getProxySimpleName() { JClassType enclosingType = baseIntf.getEnclosingType(); String enclosingTypeName = (enclosingType==null?"":enclosingType.getSimpleSourceName()+"_"); return enclosingTypeName+baseIntf.getSimpleSourceName() + "_IDB_Impl"; } /** * @return a sourceWriter for the proxy class */ @Override protected SourcePrinter getSourcePrinter() { JPackage pkg = baseIntf.getPackage(); String packageName = pkg == null ? "" : pkg.getName(); PrintWriter printWriter = context.getGeneratorContext().tryCreate(context.getLogger(), packageName, getProxySimpleName()); if (printWriter == null) { return null; } ClassSourceFileComposerFactory composerFactory = new ClassSourceFileComposerFactory(packageName, getProxySimpleName()); String[] imports = getImports(); for (String imp : imports) { composerFactory.addImport(imp); } composerFactory.setSuperclass(IDXAbstractDatabase.class.getCanonicalName()); composerFactory.addImplementedInterface(baseIntf.getQualifiedSourceName()); return new SourceCodePrinter(composerFactory.createSourceWriter(context.getGeneratorContext(), printWriter), context.getLogger()); } }