/* * Copyright 2013 Gordon Burgett and individual contributors * * 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.xflatdb.xflat.db; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.UUID; import org.xflatdb.xflat.Cursor; import org.xflatdb.xflat.DuplicateKeyException; import org.xflatdb.xflat.KeyNotFoundException; import org.xflatdb.xflat.Table; import org.xflatdb.xflat.XFlatException; import org.xflatdb.xflat.query.XPathQuery; import org.xflatdb.xflat.query.XPathUpdate; import org.jdom2.Attribute; import org.jdom2.Element; import org.xflatdb.xflat.XFlatConstants; /** * This is the simple Table implementation for tables of JDOM Elements. * @author gordon */ public class ElementTable extends TableBase implements Table<Element> { ElementTable(String tableName){ super(tableName); } @Override public void insert(Element data) throws DuplicateKeyException { String id = getId(data); if(id == null){ id = generateNewId(); setId(id, data); } //always clone incoming data final Element cData = data.clone(); final String sId = id; this.doWithEngine(new EngineActionEx<Object, DuplicateKeyException>(){ @Override public Object act(Engine engine) throws DuplicateKeyException { engine.insertRow(sId, cData); return null; } }); } @Override public Element find(Object id) { final String sId = getId(id); return this.doWithEngine(new EngineAction<Element>(){ @Override public Element act(Engine engine) { return engine.readRow(sId); } }); } private Cursor<Element> queryTable(final XPathQuery query){ return this.doWithEngine(new EngineAction<Cursor<Element>>(){ @Override public Cursor<Element> act(Engine engine) { return engine.queryTable(query); } }); } @Override public Element findOne(XPathQuery query) { try(Cursor<Element> c = this.queryTable(query)){ Iterator<Element> i = c.iterator(); if(i.hasNext()){ return i.next(); } return null; }catch(Exception ex){ throw new XFlatException("Unable to close cursor", ex); } } @Override public Cursor<Element> find(XPathQuery query) { return this.queryTable(query); } @Override public List<Element> findAll(XPathQuery query) { try(Cursor<Element> c = this.queryTable(query)){ List<Element> ret = new ArrayList<>(); for(Element e : c){ ret.add(e); } return ret; }catch(Exception ex){ throw new XFlatException("Unable to close cursor", ex); } } @Override public void replace(Element newValue) throws KeyNotFoundException { final String id = getId(newValue); if(id == null){ throw new KeyNotFoundException("Element has no ID"); } //always clone incoming data final Element data = newValue.clone(); this.doWithEngine(new EngineActionEx<Object, KeyNotFoundException>(){ @Override public Object act(Engine engine) throws KeyNotFoundException { engine.replaceRow(id, data); return null; } }); } @Override public boolean replaceOne(XPathQuery query, Element origValue){ Element e = this.findOne(query); if(e == null){ return false; } final String id = getId(e); setId(id, origValue); //always clone incoming data final Element newValue = origValue.clone(); try{ this.doWithEngine(new EngineActionEx<Object, KeyNotFoundException>(){ @Override public Object act(Engine engine) throws KeyNotFoundException { engine.replaceRow(id, newValue); return null; } }); return true; }catch(KeyNotFoundException ex){ //someone concurrently deleted this row, try again by identifying //a new row by the query. return replaceOne(query, origValue); } } @Override public boolean upsert(Element newValue) { //always clone incoming data final Element data = newValue.clone(); final String id = getId(newValue); if(id == null){ while(true){ final String nId = generateNewId(); setId(nId, newValue); try{ this.doWithEngine(new EngineActionEx<Object, DuplicateKeyException>(){ @Override public Object act(Engine engine) throws DuplicateKeyException { engine.insertRow(nId, data); return null; } }); //inserted, return true return true; } catch(DuplicateKeyException ex){ //somehow our "new" ID already existed - try again } } } else{ return this.doWithEngine(new EngineAction<Boolean>(){ @Override public Boolean act(Engine engine) { return engine.upsertRow(id, data); } }); } } @Override public boolean update(Object id, final XPathUpdate update) throws KeyNotFoundException { final String sId = getId(id); return this.doWithEngine(new EngineActionEx<Boolean, KeyNotFoundException>(){ @Override public Boolean act(Engine engine) throws KeyNotFoundException { return engine.update(sId, update); } }); } @Override public int update(final XPathQuery query, final XPathUpdate update) { return this.doWithEngine(new EngineAction<Integer>(){ @Override public Integer act(Engine engine) { return engine.update(query, update); } }); } @Override public void delete(Object id) throws KeyNotFoundException { if(id == null){ throw new IllegalArgumentException("id cannot be null"); } final String sId = getId(id); this.doWithEngine(new EngineActionEx<Object, KeyNotFoundException>(){ @Override public Object act(Engine engine) throws KeyNotFoundException { engine.deleteRow(sId); return null; } }); } @Override public int deleteAll(final XPathQuery query) { return this.doWithEngine(new EngineAction<Integer>(){ @Override public Integer act(Engine engine) { return engine.deleteAll(query); } }); } private String getId(Element element){ Attribute a = element.getAttribute("id", XFlatConstants.xFlatNs); if(a != null){ return a.getValue(); } return null; } private void setId(String id, Element e){ e.setAttribute("id", id, XFlatConstants.xFlatNs); } private String generateNewId(){ return (String)this.getIdGenerator().generateNewId(String.class); } private String getId(Object id) throws IllegalArgumentException { if(!(id instanceof String)){ throw new IllegalArgumentException("Tables of element always have string IDs"); } String sId = (String)id; return sId; } }