/* * Copyright (C) 2012 Timo Vesalainen * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.vesalainen.parsers.sql.dsql; import com.google.appengine.api.datastore.Category; import com.google.appengine.api.datastore.DatastoreService; import com.google.appengine.api.datastore.DatastoreServiceFactory; import com.google.appengine.api.datastore.Email; import com.google.appengine.api.datastore.Entity; import com.google.appengine.api.datastore.EntityNotFoundException; import com.google.appengine.api.datastore.Key; import com.google.appengine.api.datastore.Link; import com.google.appengine.api.datastore.PhoneNumber; import com.google.appengine.api.datastore.PostalAddress; import com.google.appengine.api.datastore.Rating; import com.google.appengine.api.datastore.RawValue; import com.google.appengine.api.datastore.Text; import com.google.appengine.api.mail.MailService.Message; import com.google.appengine.tools.remoteapi.RemoteApiInstaller; import com.google.appengine.tools.remoteapi.RemoteApiOptions; import java.io.IOException; import java.util.Collection; import java.util.Comparator; import java.util.Date; import java.util.List; import javax.mail.Session; import javax.mail.internet.MimeMessage; import javax.swing.ProgressMonitor; import org.vesalainen.parsers.sql.ColumnMetadata; import org.vesalainen.parsers.sql.ColumnReference; import org.vesalainen.parsers.sql.Engine; import org.vesalainen.parsers.sql.Table; import org.vesalainen.parsers.sql.InsertStatement; import org.vesalainen.parsers.sql.SQLConverter; import org.vesalainen.parsers.sql.TableContext; import org.vesalainen.parsers.sql.TableContextComparator; import org.vesalainen.parsers.sql.TableMetadata; import org.vesalainen.parsers.sql.ToFunction; import org.vesalainen.parsers.sql.Updateable; import org.vesalainen.parsers.sql.dsql.ui.I18n; /** * @author Timo Vesalainen */ public class DSQLEngine extends Engine<Entity, Object> implements DSConstants, DSProxyInterface { private DSProxyInterface proxy; private static Statistics statistics; private String email; private String namespace; private ProgressMonitor progressMonitor; private DSQLEngine(DSProxyInterface proxy) { super(DSQLParser.class); this.proxy = proxy; statistics = proxy.getStatistics(); proxy.setConverter(this); } public static DSQLEngine getInstance(String server, String namespace, String email, String password) throws IOException { RemoteApiInstaller installer = new RemoteApiInstaller(); RemoteApiOptions options = new RemoteApiOptions(); options.server(server, 443); options.credentials(email, password); installer.install(options); DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); DSQLEngine engine = getInstance(datastore); engine.namespace = namespace; engine.email = email; return engine; } public static DSQLEngine getInstance(DatastoreService datastore) { DatastoreEngine dse = new DatastoreEngine(datastore); return new DSQLEngine(dse); } public static DSQLEngine getProxyInstance(String server, String namespace, String email) throws IOException, InterruptedException { DatastoreEngineProxy dep = new DatastoreEngineProxy(server, namespace, email); dep.start(); DSQLEngine engine = new DSQLEngine(dep.getProxy()); engine.email = email; return engine; } @Override public Object convert(String string) { return string; } @Override public Object convert(Number number) { return number; } @Override public Object convertDate(Date date) { return date; } @Override public Object convertTime(Date date) { return date; } @Override public Object convertTimestamp(Date date) { return date; } @Override public Object get(Entity r, String property) { if (Entity.KEY_RESERVED_PROPERTY.equalsIgnoreCase(property)) { return r.getKey(); } if (PARENT.equalsIgnoreCase(property)) { return r.getParent(); } if (ID.equalsIgnoreCase(property)) { return r.getKey().getId(); } if (NAME.equalsIgnoreCase(property)) { return r.getKey().getName(); } Object ob = r.getProperty(property); if (ob instanceof RawValue) { RawValue rw = (RawValue) ob; return rw.getValue(); } return ob; } @Override public void set(Entity r, String column, Object value) { if (r.isUnindexedProperty(column)) { r.setUnindexedProperty(column, value); } else { r.setProperty(column, value); } } @Override public Comparator<Object> getComparator() { return new DSQLObjectComparator(this); } @Override protected Table<Entity,Object> createTable(String schema, String tablename, String correlationName) { return new DSTable(this, schema, tablename, correlationName); } @Override public TableMetadata getTableMetadata(String tablename) { return statistics.getKind(tablename); } @Override protected TableContextComparator getTableContextComparator() { return new DSTableContextComparator(statistics, this); } @Override public Iterable<TableMetadata> getTables() { return statistics.getTables(); } @Override public Updateable<Entity, Object> getUpdateable(Entity entity, String property, Object value) { assert entity.hasProperty(property) && !(entity.getProperty(property) instanceof RawValue); boolean indexed = false; ColumnMetadata cm = statistics.getProperty(entity.getKind(), property); if (cm != null) { indexed = cm.isIndexed(); } if (value != null) { if (!value.equals(entity.getProperty(property))) { if (value.getClass().isInstance(entity.getProperty(property))) { if (indexed) { entity.setProperty(property, value); } else { entity.setUnindexedProperty(property, value); } } else { throw new IllegalArgumentException(String.format(I18n.get("UPDATING THROUGH FUNCTION ATTEMPS TO CHANGE x TYPE"), property)); } } } return new UpdateableImpl(entity, property, indexed); } @Override public void update(Collection<Entity> rows) { proxy.update(rows); } @Override public void setConverter(SQLConverter converter) { proxy.setConverter(converter); } public void rollbackTransaction() { proxy.rollbackTransaction(); } public void insert(InsertStatement insertStatement) { proxy.insert(insertStatement); } public Statistics getStatistics() { return proxy.getStatistics(); } @Override public Collection<Entity> fetch(TableContext<Entity, Object> tc, boolean update) { return proxy.fetch(tc, update); } public Collection<Entity> fetch(Table<Entity, Object> table) { return proxy.fetch(table); } public void exit() { proxy.exit(); } public void delete(Collection<Entity> rows) { proxy.delete(rows); } public void commitTransaction() { proxy.commitTransaction(); } public void beginTransaction() { proxy.beginTransaction(); } public Key stringToKey(String encoded) { return proxy.stringToKey(encoded); } public String keyToString(Key key) { return proxy.keyToString(key); } public String createKeyString(String kind, String name) { return proxy.createKeyString(kind, name); } public String createKeyString(String kind, long id) { return proxy.createKeyString(kind, id); } public String createKeyString(Key parent, String kind, String name) { return proxy.createKeyString(parent, kind, name); } public String createKeyString(Key parent, String kind, long id) { return proxy.createKeyString(parent, kind, id); } public Key createKey(String kind, String name) { return proxy.createKey(kind, name); } public Key createKey(String kind, long id) { return proxy.createKey(kind, id); } public Key createKey(Key parent, String kind, String name) { return proxy.createKey(parent, kind, name); } public Key createKey(Key parent, String kind, long id) { return proxy.createKey(parent, kind, id); } @Override public Class<? extends Object> getDefaultPlaceholderType() { return String.class; } public String getEmail() { return email; } @Override public void send(Message message) throws IOException { proxy.send(message); } @Override public Session getSession() { return proxy.getSession(); } @Override public void send(MimeMessage message) throws IOException { proxy.send(message); } @Override public Entity get(Key key) throws EntityNotFoundException { return proxy.get(key); } @Override public void update(Entity row) { proxy.update(row); } @Override public void delete(Entity row) { proxy.delete(row); } @Override public ColumnReference createFunction(ColumnReference inner, String funcName, String... args) { switch (funcName.toLowerCase()) { case "toemail": check(funcName, args.length, 0, 0); return new ToFunction(inner, Email.class); case "tolink": check(funcName, args.length, 0, 0); return new ToFunction(inner, Link.class); case "tophonenumber": check(funcName, args.length, 0, 0); return new ToFunction(inner, PhoneNumber.class); case "totext": check(funcName, args.length, 0, 0); return new ToFunction(inner, Text.class); case "topostaladdress": check(funcName, args.length, 0, 0); return new ToFunction(inner, PostalAddress.class); case "tocategory": check(funcName, args.length, 0, 0); return new ToFunction(inner, Category.class); case "torating": check(funcName, args.length, 0, 0); return new ToFunction(inner, Rating.class); default: try { return super.createFunction(inner, funcName, args); } catch (IllegalArgumentException ex) { throw new IllegalArgumentException("expected toemail tophonenumber got "+funcName, ex); } } } @Override public List<Entity> getAll(String kind) { return proxy.getAll(kind); } @Override public void createProgressMonitor(Object ob) { progressMonitor = (ProgressMonitor) ob; } @Override public void destroyProgressMonitor() { progressMonitor = null; } @Override protected void startProgressMonitor(int min, int max) { if (progressMonitor != null) { System.err.println("startProgressMonitor("+min+", "+max+")"); progressMonitor.setMinimum(min); progressMonitor.setMaximum(max); } } @Override protected void progressNote(String note) { if (progressMonitor != null) { System.err.println("progressNote("+note+")"); progressMonitor.setNote(note); } } @Override protected void updateProgressMonitor(int now) { if (progressMonitor != null) { System.err.println("updateProgressMonitor("+now+")"); progressMonitor.setProgress(now); } } }