/******************************************************************************* * Copyright (c) 2012 IT Just working. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IT Just working - initial API and implementation *******************************************************************************/ package com.minres.scviewer.database.text; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.zip.GZIPInputStream; import org.eclipse.collections.impl.map.mutable.UnifiedMap; import org.mapdb.DB; import org.mapdb.DB.TreeMapSink; import org.mapdb.DBMaker; import org.mapdb.Serializer; import com.google.common.collect.HashMultimap; import com.minres.scviewer.database.AssociationType; import com.minres.scviewer.database.DataType; import com.minres.scviewer.database.EventKind; import com.minres.scviewer.database.IWaveform; import com.minres.scviewer.database.IWaveformDb; import com.minres.scviewer.database.IWaveformDbLoader; import com.minres.scviewer.database.InputFormatException; import com.minres.scviewer.database.RelationType; import com.minres.scviewer.database.RelationTypeFactory; import com.minres.scviewer.database.tx.ITx; public class TextDbLoader implements IWaveformDbLoader{ private Long maxTime=0L; DB mapDb=null; final List attrValues = new ArrayList<>(); final Map relationTypes = UnifiedMap.newMap(); final Map txStreams = UnifiedMap.newMap(); final Map txGenerators = UnifiedMap.newMap(); Map transactions = null; final Map attributeTypes = UnifiedMap.newMap(); final HashMultimap relationsIn = HashMultimap.create(); final HashMultimap relationsOut = HashMultimap.create(); HashMap txCache = new HashMap<>(); List threads = new ArrayList<>(); @Override public Long getMaxTime() { return maxTime; } @Override public Collection getAllWaves() { return new ArrayList<>(txStreams.values()); } static final byte[] x = "scv_tr_stream".getBytes(); @SuppressWarnings("unchecked") @Override public boolean load(IWaveformDb db, File file) throws InputFormatException { dispose(); if(file.isDirectory() || !file.exists()) return false; TextDbParser parser = new TextDbParser(this); boolean gzipped = isGzipped(file); try { if(!isTxfile(gzipped?new GZIPInputStream(new FileInputStream(file)):new FileInputStream(file))) return false; } catch(Exception e) { throw new InputFormatException(); } if(file.length() < 75000000*(gzipped?1:10) || "memory".equals(System.getProperty("ScvBackingDB", "file"))) mapDb = DBMaker .memoryDirectDB() .allocateStartSize(512l*1024l*1024l) .allocateIncrement(128l*1024l*1024l) .cleanerHackEnable() .make(); else { File mapDbFile; try { mapDbFile = File.createTempFile("."+file.getName(), ".mapdb", null /*file.parentFile*/); Files.delete(Paths.get(mapDbFile.getPath())); } catch (IOException e1) { return false; } mapDb = DBMaker .fileDB(mapDbFile) .fileMmapEnable() // Always enable mmap .fileMmapEnableIfSupported() .fileMmapPreclearDisable() .allocateStartSize(512l*1024l*1024l) .allocateIncrement(128l*1024l*1024l) .cleanerHackEnable() .make(); mapDbFile.deleteOnExit(); } try { parser.txSink = mapDb.treeMap("transactions", Serializer.LONG,Serializer.JAVA).createFromSink(); parser.parseInput(gzipped?new GZIPInputStream(new FileInputStream(file)):new FileInputStream(file)); transactions = parser.txSink.create(); } catch(IllegalArgumentException|ArrayIndexOutOfBoundsException e) { } catch(Exception e) { System.out.println("---->>> Exception "+e.toString()+" caught while loading database"); e.printStackTrace(); return false; } for(TxStream stream:txStreams.values()) { Thread t = new Thread() { @Override public void run() { try { stream.calculateConcurrency(); } catch (Exception e) {/* don't let exceptions bubble up */ } } }; threads.add(t); t.start(); } return true; } @Override public void dispose() { attrValues.clear(); relationTypes.clear(); txStreams.clear(); txGenerators.clear(); transactions = null; attributeTypes.clear(); relationsIn.clear(); relationsOut.clear(); if(mapDb!=null) { mapDb.close(); mapDb=null; } } private static boolean isTxfile(InputStream istream) { byte[] buffer = new byte[x.length]; try { int readCnt = istream.read(buffer, 0, x.length); istream.close(); if(readCnt==x.length){ for(int i=0; i getAllRelationTypes(){ return relationTypes.values(); } static class TextDbParser { static final Pattern scv_tr_stream = Pattern.compile("^scv_tr_stream\\s+\\(ID (\\d+),\\s+name\\s+\"([^\"]+)\",\\s+kind\\s+\"([^\"]+)\"\\)$"); static final Pattern scv_tr_generator = Pattern.compile("^scv_tr_generator\\s+\\(ID\\s+(\\d+),\\s+name\\s+\"([^\"]+)\",\\s+scv_tr_stream\\s+(\\d+),$"); static final Pattern begin_attribute = Pattern.compile("^begin_attribute \\(ID (\\d+), name \"([^\"]+)\", type \"([^\"]+)\"\\)$"); static final Pattern end_attribute = Pattern.compile("^end_attribute \\(ID (\\d+), name \"([^\"]+)\", type \"([^\"]+)\"\\)$"); final TextDbLoader loader; HashMap transactionById = new HashMap<>(); TreeMapSink txSink; BufferedReader reader = null; TxGenerator generator=null; Map attrValueLut = new HashMap<>(); public TextDbParser(TextDbLoader loader) { super(); this.loader = loader; } void parseInput(InputStream inputStream) throws IOException{ reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); String curLine = reader.readLine(); String nextLine = null; while((nextLine=reader.readLine())!=null && curLine!=null) { curLine=parseLine(curLine, nextLine); } if(curLine!=null) parseLine(curLine, nextLine); } private TxAttributeType getAttrType(String name, DataType dataType, AssociationType type){ String key =name+"-"+dataType.toString(); TxAttributeType res; if(loader.attributeTypes.containsKey(key)){ res=loader.attributeTypes.get(key); } else { res=new TxAttributeType(name, dataType, type); loader.attributeTypes.put(key, res); } return res; } private String parseLine(String curLine, String nextLine) throws IOException{ String[] tokens = curLine.split("\\s+"); if("tx_record_attribute".equals(tokens[0])){ Long id = Long.parseLong(tokens[1]); String name = tokens[2].substring(1, tokens[2].length()); DataType type = DataType.valueOf(tokens[3]); String remaining = tokens.length>5?String.join(" ", Arrays.copyOfRange(tokens, 5, tokens.length)):""; TxAttributeType attrType = getAttrType(name, type, AssociationType.RECORD); transactionById.get(id).attributes.add(new TxAttribute(attrType, getAttrString(attrType, remaining))); } else if("tx_begin".equals(tokens[0])){ Long id = Long.parseLong(tokens[1]); Long genId = Long.parseLong(tokens[2]); TxGenerator gen=loader.txGenerators.get(genId); ScvTx scvTx = new ScvTx(id, gen.stream.getId(), genId, Long.parseLong(tokens[3])*stringToScale(tokens[4])); loader.maxTime = loader.maxTime>scvTx.beginTime?loader.maxTime:scvTx.beginTime; TxStream stream = loader.txStreams.get(gen.stream.getId()); stream.setConcurrency(stream.getConcurrency()+1); if(nextLine!=null && nextLine.charAt(0)=='a') { int idx=0; while(nextLine!=null && nextLine.charAt(0)=='a') { String[] attrTokens=nextLine.split("\\s+"); TxAttributeType attrType = gen.beginAttrs.get(idx); TxAttribute attr = new TxAttribute(attrType, getAttrString(attrType, attrTokens[1])); scvTx.attributes.add(attr); idx++; nextLine=reader.readLine(); } } txSink.put(id, scvTx); transactionById.put(id, scvTx); } else if("tx_end".equals(tokens[0])){ Long id = Long.parseLong(tokens[1]); ScvTx scvTx = transactionById.get(id); assert Long.parseLong(tokens[2])==scvTx.generatorId; scvTx.endTime=Long.parseLong(tokens[3])*stringToScale(tokens[4]); loader.maxTime = loader.maxTime>scvTx.endTime?loader.maxTime:scvTx.endTime; TxGenerator gen = loader.txGenerators.get(scvTx.generatorId); TxStream stream = loader.txStreams.get(gen.stream.getId()); if(scvTx.beginTime==scvTx.endTime) stream.addEvent(new TxEvent(loader, EventKind.SINGLE, id, scvTx.beginTime)); else { stream.addEvent(new TxEvent(loader, EventKind.BEGIN, id, scvTx.beginTime)); stream.addEvent(new TxEvent(loader, EventKind.END, id, scvTx.endTime)); } stream.setConcurrency(stream.getConcurrency()-1); if(nextLine!=null && nextLine.charAt(0)=='a') { int idx=0; while(nextLine!=null && nextLine.charAt(0)=='a') { String[] attrTokens=nextLine.split("\\s+"); TxAttributeType attrType = gen.endAttrs.get(idx); TxAttribute attr = new TxAttribute(attrType, getAttrString(attrType, attrTokens[1])); scvTx.attributes.add(attr); idx++; nextLine=reader.readLine(); } } } else if("tx_relation".equals(tokens[0])){ Long tr2= Long.parseLong(tokens[2]); Long tr1= Long.parseLong(tokens[3]); String relType=tokens[1].substring(1, tokens[1].length()-1); if(!loader.relationTypes.containsKey(relType)) loader.relationTypes.put(relType, RelationTypeFactory.create(relType)); ScvRelation rel = new ScvRelation(loader.relationTypes.get(relType), tr1, tr2); loader.relationsOut.put(tr1, rel); loader.relationsIn.put(tr2, rel); } else if("scv_tr_stream".equals(tokens[0])){ Matcher matcher = scv_tr_stream.matcher(curLine); if (matcher.matches()) { Long id = Long.parseLong(matcher.group(1)); TxStream stream = new TxStream(loader, id, matcher.group(2), matcher.group(3)); loader.txStreams.put(id, stream); } } else if("scv_tr_generator".equals(tokens[0])){ Matcher matcher = scv_tr_generator.matcher(curLine); if ((matcher.matches())) { Long id = Long.parseLong(matcher.group(1)); TxStream stream=loader.txStreams.get(Long.parseLong(matcher.group(3))); generator=new TxGenerator(loader, id, matcher.group(2), stream); loader.txGenerators.put(id, generator); } } else if("begin_attribute".equals(tokens[0])){ Matcher matcher = begin_attribute.matcher(curLine); if ((matcher.matches())) { TxAttributeType attrType = getAttrType(matcher.group(2), DataType.valueOf(matcher.group(3)), AssociationType.BEGIN); generator.beginAttrs.add(attrType); } } else if("end_attribute".equals(tokens[0])){ Matcher matcher = end_attribute.matcher(curLine); if ((matcher.matches())) { TxAttributeType attrType = getAttrType(matcher.group(2), DataType.valueOf(matcher.group(3)), AssociationType.END); generator.endAttrs.add(attrType); } } else if(")".equals(tokens[0])){ generator=null; } else if("a".equals(tokens[0])){//matcher = line =~ /^a\s+(.+)$/ System.out.println("Don't know what to do with: '"+curLine+"'"); } else System.out.println("Don't know what to do with: '"+curLine+"'"); return nextLine; } private String getAttrString(TxAttributeType attrType, String string) { String value; switch(attrType.getDataType()){ case STRING: case ENUMERATION: value=string.substring(1, string.length()-1); break; default: value=string; } if(attrValueLut.containsKey(value)){ return loader.attrValues.get(attrValueLut.get(value)); } else { attrValueLut.put(value, loader.attrValues.size()); loader.attrValues.add(value); return value; } } private long stringToScale(String scale){ String cmp = scale.trim(); if("fs".equals(cmp)) return 1L; if("ps".equals(cmp)) return 1000L; if("ns".equals(cmp)) return 1000000L; if("us".equals(cmp)) return 1000000000L; if("ms".equals(cmp)) return 1000000000000L; if("s".equals(cmp) ) return 1000000000000000L; return 1L; } } public ITx getTransaction(long txId) { if(txCache.containsKey(txId)) return txCache.get(txId); Tx tx = new Tx(this, txId); txCache.put(txId, tx); return tx; } }