refactor internal transaction representation
This commit is contained in:
parent
b44f3418f4
commit
85ce07f11f
|
@ -0,0 +1,25 @@
|
|||
package com.minres.scviewer.database.text;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.minres.scviewer.database.RelationType;
|
||||
|
||||
class ScvRelation implements Serializable {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = -347668857680574140L;
|
||||
|
||||
final long source;
|
||||
|
||||
final long target;
|
||||
|
||||
final RelationType relationType;
|
||||
|
||||
public ScvRelation(RelationType relationType, long source, long target) {
|
||||
this.source = source;
|
||||
this.target = target;
|
||||
this.relationType = relationType;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*******************************************************************************
|
||||
* 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.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.minres.scviewer.database.tx.ITxAttribute;
|
||||
|
||||
class ScvTx implements Serializable{
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = -855200240003328221L;
|
||||
|
||||
final long id;
|
||||
|
||||
final long generatorId;
|
||||
|
||||
final long streamId;
|
||||
|
||||
long beginTime;
|
||||
|
||||
long endTime;
|
||||
|
||||
final List<ITxAttribute> attributes = new ArrayList<>();
|
||||
|
||||
ScvTx(long id, long streamId, long generatorId, long begin){
|
||||
this.id=id;
|
||||
this.streamId=streamId;
|
||||
this.generatorId=generatorId;
|
||||
this.beginTime=begin;
|
||||
this.endTime=begin;
|
||||
}
|
||||
|
||||
Long getId() {return id;}
|
||||
}
|
|
@ -11,7 +11,6 @@
|
|||
package com.minres.scviewer.database.text;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.EOFException;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
|
@ -21,69 +20,72 @@ 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.TreeMap;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
import org.mapdb.BTreeMap;
|
||||
import org.mapdb.DB;
|
||||
import org.mapdb.DB.TreeMapSink;
|
||||
import org.mapdb.DBMaker;
|
||||
import org.mapdb.Serializer;
|
||||
import org.mapdb.serializer.SerializerArrayTuple;
|
||||
|
||||
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.tx.ITxGenerator;
|
||||
import com.minres.scviewer.database.tx.ITx;
|
||||
import com.minres.scviewer.database.tx.ITxAttributeType;
|
||||
|
||||
public class TextDbLoader implements IWaveformDbLoader{
|
||||
|
||||
private Long maxTime=0L;
|
||||
|
||||
IWaveformDb db;
|
||||
|
||||
List<IWaveform> streams;
|
||||
|
||||
Map<String, RelationType> relationTypes=new HashMap<String, RelationType>();
|
||||
|
||||
DB mapDb;
|
||||
|
||||
public TextDbLoader() {
|
||||
}
|
||||
TreeMap<Long, TxStream> txStreams;
|
||||
|
||||
@Override
|
||||
TreeMap<Long, TxGenerator> txGenerators;
|
||||
|
||||
BTreeMap<Long, ScvTx> transactions;
|
||||
|
||||
BTreeMap<Long[], ScvRelation> relations;
|
||||
|
||||
BTreeMap<String[], TxAttributeType> attributeTypes;
|
||||
|
||||
HashMap<Long, Tx> txCache = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public Long getMaxTime() {
|
||||
return maxTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<IWaveform> getAllWaves() {
|
||||
return streams;
|
||||
}
|
||||
|
||||
public Map<Long, ITxGenerator> getGeneratorsById() {
|
||||
TreeMap<Long, ITxGenerator> res = new TreeMap<Long, ITxGenerator>();
|
||||
for(IWaveform stream: streams){ for(ITxGenerator it: ((TxStream)stream).getGenerators()){res.put(it.getId(), it);} }
|
||||
return res;
|
||||
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 {
|
||||
if(file.isDirectory() || !file.exists()) return false;
|
||||
this.db=db;
|
||||
this.streams = new ArrayList<>();
|
||||
TextDbParser parser = new TextDbParser(this);
|
||||
boolean gzipped = isGzipped(file);
|
||||
try {
|
||||
if(!isTxfile(gzipped?new GZIPInputStream(new FileInputStream(file)):new FileInputStream(file)))
|
||||
return false;
|
||||
} catch(Throwable e) {
|
||||
} catch(Exception e) {
|
||||
throw new InputFormatException();
|
||||
}
|
||||
File mapDbFile;
|
||||
|
@ -94,7 +96,7 @@ public class TextDbLoader implements IWaveformDbLoader{
|
|||
}
|
||||
mapDbFile.delete(); // we just need a file name
|
||||
mapDbFile.deleteOnExit();
|
||||
this.mapDb = DBMaker
|
||||
mapDb = DBMaker
|
||||
.fileDB(mapDbFile)
|
||||
.fileMmapEnableIfSupported()
|
||||
.fileMmapPreclearDisable()
|
||||
|
@ -102,15 +104,26 @@ public class TextDbLoader implements IWaveformDbLoader{
|
|||
.allocateStartSize(64*1024*1024)
|
||||
.allocateIncrement(64*1024*1024)
|
||||
.make();
|
||||
TreeMapSink<Long, ScvTx> txSink = mapDb.treeMap("transactions", Serializer.LONG,Serializer.JAVA).createFromSink();
|
||||
relations = mapDb
|
||||
.treeMap("relations", new SerializerArrayTuple(Serializer.LONG, Serializer.LONG) ,Serializer.JAVA)
|
||||
.createOrOpen();
|
||||
attributeTypes = mapDb
|
||||
.treeMap("attributeTypes", new SerializerArrayTuple(Serializer.STRING, Serializer.STRING) ,Serializer.JAVA)
|
||||
.createOrOpen();
|
||||
try {
|
||||
parser.parseInput(gzipped?new GZIPInputStream(new FileInputStream(file)):new FileInputStream(file));
|
||||
parser.setTransactionSink(txSink).setRelationMap(relations);
|
||||
parser.parseInput(gzipped?new GZIPInputStream(new FileInputStream(file)):new FileInputStream(file));
|
||||
} catch(IllegalArgumentException|ArrayIndexOutOfBoundsException e) {
|
||||
} catch(Throwable e) {
|
||||
} catch(Exception e) {
|
||||
System.out.println("---->>> Exception "+e.toString()+" caught while loading database");
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
streams.addAll(parser.streamsById.values());
|
||||
transactions=txSink.create();
|
||||
txStreams=new TreeMap<>(parser.streamsById);
|
||||
txGenerators=new TreeMap<>(parser.generatorsById);
|
||||
txCache.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -149,20 +162,37 @@ public class TextDbLoader implements IWaveformDbLoader{
|
|||
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 \"([^\"]+)\"\\)$");
|
||||
|
||||
HashMap<Long, TxStream> streamsById = new HashMap<Long, TxStream>();
|
||||
HashMap<Long, TxGenerator> generatorsById = new HashMap<Long, TxGenerator>();
|
||||
HashMap<Long, Tx> transactionsById = new HashMap<Long, Tx>();
|
||||
TxGenerator generator = null;
|
||||
Tx transaction = null;
|
||||
boolean endTransaction=false;
|
||||
HashMap<Long, TxStream> streamsById = new HashMap<>();
|
||||
|
||||
HashMap<Long, TxGenerator> generatorsById = new HashMap<>();
|
||||
|
||||
HashMap<Long, ScvTx> transactionsById = new HashMap<>();
|
||||
|
||||
final TextDbLoader loader;
|
||||
BufferedReader reader =null;
|
||||
|
||||
BufferedReader reader = null;
|
||||
|
||||
TxGenerator generator = null;
|
||||
|
||||
private TreeMapSink<Long, ScvTx> txSink;
|
||||
|
||||
private BTreeMap<Long[], ScvRelation> relations;
|
||||
|
||||
public TextDbParser(TextDbLoader loader) {
|
||||
super();
|
||||
this.loader = loader;
|
||||
}
|
||||
|
||||
public TextDbParser setTransactionSink(TreeMapSink<Long, ScvTx> sink) {
|
||||
this.txSink=sink;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TextDbParser setRelationMap(BTreeMap<Long[], ScvRelation> relations) {
|
||||
this.relations=relations;
|
||||
return this;
|
||||
}
|
||||
|
||||
void parseInput(InputStream inputStream) throws IOException{
|
||||
reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
|
||||
String curLine = reader.readLine();
|
||||
|
@ -174,6 +204,18 @@ public class TextDbLoader implements IWaveformDbLoader{
|
|||
parseLine(curLine, nextLine);
|
||||
}
|
||||
|
||||
private TxAttributeType getAttrType(String name, DataType dataType, AssociationType type){
|
||||
String[] key = new String[] {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])){//matcher = line =~ /^tx_record_attribute\s+(\d+)\s+"([^"]+)"\s+(\S+)\s*=\s*(.+)$/
|
||||
|
@ -181,54 +223,62 @@ public class TextDbLoader implements IWaveformDbLoader{
|
|||
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-1)):"";
|
||||
transactionsById.get(id).getAttributes().add(new TxAttribute(name, type, AssociationType.RECORD, remaining));
|
||||
TxAttributeType attrType = getAttrType(name, type, AssociationType.RECORD);
|
||||
transactionsById.get(id).attributes.add(new TxAttribute(attrType, remaining));
|
||||
} else if("tx_begin".equals(tokens[0])){
|
||||
Long id = Long.parseLong(tokens[1]);
|
||||
TxGenerator gen=generatorsById.get(Long.parseLong(tokens[2]));
|
||||
TxStream stream = (TxStream) gen.getStream();
|
||||
Long genId = Long.parseLong(tokens[2]);
|
||||
TxGenerator gen=generatorsById.get(genId);
|
||||
ScvTx tx = new ScvTx(id, gen.stream.getId(), genId, Long.parseLong(tokens[3])*stringToScale(tokens[4]));
|
||||
transactionsById.put(id, tx);
|
||||
loader.maxTime = loader.maxTime>tx.beginTime?loader.maxTime:tx.beginTime;
|
||||
TxStream stream = streamsById.get(gen.stream.getId());
|
||||
stream.setConcurrency(stream.getConcurrency()+1);
|
||||
transaction = new Tx(id, gen.getStream(), gen, Long.parseLong(tokens[3])*stringToScale(tokens[4]));
|
||||
gen.getTransactions().add(transaction);
|
||||
transactionsById.put(id, transaction);
|
||||
loader.maxTime = loader.maxTime>transaction.getBeginTime()?loader.maxTime:transaction.getBeginTime();
|
||||
if(nextLine!=null && nextLine.charAt(0)=='a') {
|
||||
int idx=0;
|
||||
while(nextLine!=null && nextLine.charAt(0)=='a') {
|
||||
String[] attrTokens=nextLine.split("\\s+");
|
||||
TxAttribute attr = new TxAttribute(gen.getBeginAttrs().get(idx), attrTokens[1]);
|
||||
transaction.getAttributes().add(attr);
|
||||
TxAttribute attr = new TxAttribute(gen.beginAttrs.get(idx), attrTokens[1]);
|
||||
tx.attributes.add(attr);
|
||||
idx++;
|
||||
nextLine=reader.readLine();
|
||||
}
|
||||
}
|
||||
txSink.put(tx.id, tx);
|
||||
} else if("tx_end".equals(tokens[0])){//matcher = line =~ /^tx_end\s+(\d+)\s+(\d+)\s+(\d+)\s+([munpf]?s)/
|
||||
Long id = Long.parseLong(tokens[1]);
|
||||
transaction = transactionsById.get(id);
|
||||
assert Integer.parseInt(tokens[2])==transaction.getGenerator().getId();
|
||||
transaction.setEndTime(Long.parseLong(tokens[3])*stringToScale(tokens[4]));
|
||||
loader.maxTime = loader.maxTime>transaction.getEndTime()?loader.maxTime:transaction.getEndTime();
|
||||
TxGenerator gen = (TxGenerator) transaction.getGenerator();
|
||||
TxStream stream = (TxStream) gen.getStream();
|
||||
ScvTx tx = transactionsById.get(id);
|
||||
assert Long.parseLong(tokens[2])==tx.generatorId;
|
||||
tx.endTime=Long.parseLong(tokens[3])*stringToScale(tokens[4]);
|
||||
loader.maxTime = loader.maxTime>tx.endTime?loader.maxTime:tx.endTime;
|
||||
TxGenerator gen = generatorsById.get(tx.generatorId);
|
||||
TxStream stream = streamsById.get(gen.stream.getId());
|
||||
if(tx.beginTime==tx.endTime)
|
||||
stream.addEvent(new TxEvent(loader, EventKind.SINGLE, id, tx.beginTime));
|
||||
else {
|
||||
stream.addEvent(new TxEvent(loader, EventKind.BEGIN, id, tx.beginTime));
|
||||
stream.addEvent(new TxEvent(loader, EventKind.END, id, tx.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+");
|
||||
TxAttribute attr = new TxAttribute(gen.getEndAttrs().get(idx), attrTokens[1]);
|
||||
transaction.getAttributes().add(attr);
|
||||
TxAttribute attr = new TxAttribute(gen.endAttrs.get(idx), attrTokens[1]);
|
||||
tx.attributes.add(attr);
|
||||
idx++;
|
||||
nextLine=reader.readLine();
|
||||
}
|
||||
}
|
||||
transactionsById.remove(tx.id);
|
||||
} else if("tx_relation".equals(tokens[0])){//matcher = line =~ /^tx_relation\s+\"(\S+)\"\s+(\d+)\s+(\d+)$/
|
||||
Tx tr2= transactionsById.get(Long.parseLong(tokens[2]));
|
||||
Tx tr1= transactionsById.get(Long.parseLong(tokens[3]));
|
||||
Long tr2= Long.parseLong(tokens[2]);
|
||||
Long tr1= Long.parseLong(tokens[3]);
|
||||
String relType=tokens[1].substring(1, tokens[1].length()-2);
|
||||
if(!loader.relationTypes.containsKey(relType))
|
||||
loader.relationTypes.put(relType, RelationType.create(relType));
|
||||
TxRelation rel = new TxRelation(loader.relationTypes.get(relType), tr1, tr2);
|
||||
tr1.getOutgoingRelations().add(rel);
|
||||
tr2.getIncomingRelations().add(rel);
|
||||
ScvRelation rel = new ScvRelation(loader.relationTypes.get(relType), tr1, tr2);
|
||||
relations.put(new Long[]{tr1, tr2}, rel);
|
||||
} else if("scv_tr_stream".equals(tokens[0])){
|
||||
Matcher matcher = scv_tr_stream.matcher(curLine);
|
||||
if (matcher.matches()) {
|
||||
|
@ -242,18 +292,19 @@ public class TextDbLoader implements IWaveformDbLoader{
|
|||
Long id = Long.parseLong(matcher.group(1));
|
||||
TxStream stream=streamsById.get(Long.parseLong(matcher.group(3)));
|
||||
generator=new TxGenerator(id, stream, matcher.group(2));
|
||||
stream.getGenerators().add(generator);
|
||||
generatorsById.put(id, generator);
|
||||
}
|
||||
} else if("begin_attribute".equals(tokens[0])){
|
||||
Matcher matcher = begin_attribute.matcher(curLine);
|
||||
if ((matcher.matches())) {
|
||||
generator.getBeginAttrs().add(TxAttributeType.getAttrType(matcher.group(2), DataType.valueOf(matcher.group(3)), AssociationType.BEGIN));
|
||||
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())) {
|
||||
generator.getEndAttrs().add(TxAttributeType.getAttrType(matcher.group(2), DataType.valueOf(matcher.group(3)), AssociationType.END));
|
||||
TxAttributeType attrType = getAttrType(matcher.group(2), DataType.valueOf(matcher.group(3)), AssociationType.END);
|
||||
generator.endAttrs.add(attrType);
|
||||
}
|
||||
} else if(")".equals(tokens[0])){
|
||||
generator=null;
|
||||
|
@ -275,5 +326,13 @@ public class TextDbLoader implements IWaveformDbLoader{
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,67 +10,49 @@
|
|||
*******************************************************************************/
|
||||
package com.minres.scviewer.database.text;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.NavigableMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.minres.scviewer.database.*;
|
||||
import com.minres.scviewer.database.IWaveform;
|
||||
import com.minres.scviewer.database.tx.ITx;
|
||||
import com.minres.scviewer.database.tx.ITxAttribute;
|
||||
import com.minres.scviewer.database.tx.ITxGenerator;
|
||||
import com.minres.scviewer.database.tx.ITxRelation;
|
||||
|
||||
class Tx implements ITx, Serializable{
|
||||
class Tx implements ITx {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = -855200240003328221L;
|
||||
private final TextDbLoader loader;
|
||||
|
||||
private final Long id;
|
||||
|
||||
private final TxGenerator generator;
|
||||
|
||||
private final IWaveform stream;
|
||||
private ScvTx scvTx = null;
|
||||
|
||||
private int concurrencyIndex;
|
||||
|
||||
private final Long beginTime;
|
||||
|
||||
private Long endTime;
|
||||
|
||||
private final List<ITxAttribute> attributes = new ArrayList<>();
|
||||
|
||||
private final List<ITxRelation> incomingRelations = new ArrayList<>();
|
||||
|
||||
private final List<ITxRelation> outgoingRelations = new ArrayList<>();
|
||||
|
||||
Tx(Long id, IWaveform stream, TxGenerator generator, Long begin){
|
||||
this.id=id;
|
||||
this.stream=stream;
|
||||
this.generator=generator;
|
||||
this.beginTime=begin;
|
||||
this.endTime=begin;
|
||||
Tx(TextDbLoader loader, Long id){
|
||||
this.loader=loader;
|
||||
this.scvTx = loader.transactions.get(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ITxRelation> getIncomingRelations() {
|
||||
return incomingRelations;
|
||||
NavigableMap<Long[], ScvRelation> rels = loader.relations.prefixSubMap(new Long[]{scvTx.getId(), null});
|
||||
return rels.values().stream().map(rel -> new TxRelation(loader, rel)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ITxRelation> getOutgoingRelations() {
|
||||
return outgoingRelations;
|
||||
NavigableMap<Long[], ScvRelation> rels = loader.relations.prefixSubMap(new Long[]{null, scvTx.getId()});
|
||||
return rels.values().stream().map(rel -> new TxRelation(loader, rel)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(ITx o) {
|
||||
int res =beginTime.compareTo(o.getBeginTime());
|
||||
int res =getBeginTime().compareTo(o.getBeginTime());
|
||||
if(res!=0)
|
||||
return res;
|
||||
else
|
||||
return id.compareTo(o.getId());
|
||||
return getId().compareTo(o.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -80,31 +62,27 @@ class Tx implements ITx, Serializable{
|
|||
|
||||
@Override
|
||||
public Long getId() {
|
||||
return id;
|
||||
return scvTx.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IWaveform getStream() {
|
||||
return stream;
|
||||
return loader.txStreams.get(scvTx.streamId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITxGenerator getGenerator() {
|
||||
return generator;
|
||||
return loader.txGenerators.get(scvTx.generatorId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getBeginTime() {
|
||||
return beginTime;
|
||||
return scvTx.beginTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getEndTime() {
|
||||
return endTime;
|
||||
}
|
||||
|
||||
public void setEndTime(long l) {
|
||||
endTime=l;
|
||||
return scvTx.endTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -118,7 +96,7 @@ class Tx implements ITx, Serializable{
|
|||
|
||||
@Override
|
||||
public List<ITxAttribute> getAttributes() {
|
||||
return attributes;
|
||||
return scvTx.attributes;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@ import java.io.Serializable;
|
|||
import com.minres.scviewer.database.AssociationType;
|
||||
import com.minres.scviewer.database.DataType;
|
||||
import com.minres.scviewer.database.tx.ITxAttribute;
|
||||
import com.minres.scviewer.database.tx.ITxAttributeType;
|
||||
|
||||
public class TxAttribute implements ITxAttribute, Serializable {
|
||||
|
||||
|
@ -24,28 +23,24 @@ public class TxAttribute implements ITxAttribute, Serializable {
|
|||
*/
|
||||
private static final long serialVersionUID = 4767726016651807152L;
|
||||
|
||||
ITxAttributeType attributeType;
|
||||
private TxAttributeType attributeType;
|
||||
|
||||
String value;
|
||||
private String value=null;
|
||||
|
||||
TxAttribute(String name, DataType dataType, AssociationType type, String value){
|
||||
attributeType = TxAttributeTypeFactory.INSTANCE.getAttrType(name, dataType, type);
|
||||
switch(dataType){
|
||||
case STRING:
|
||||
case ENUMERATION:
|
||||
this.value=value.substring(1, value.length()-2);
|
||||
break;
|
||||
default:
|
||||
this.value=value;
|
||||
}
|
||||
TxAttribute(TxAttributeType attributeType){
|
||||
this.attributeType=attributeType;
|
||||
}
|
||||
|
||||
TxAttribute(ITxAttributeType type){
|
||||
TxAttribute(TxAttributeType type, String value){
|
||||
attributeType=type;
|
||||
}
|
||||
|
||||
TxAttribute(ITxAttributeType type, String value){
|
||||
this(type.getName(), type.getDataType(), type.getType(), value);
|
||||
switch(type.getDataType()){
|
||||
case STRING:
|
||||
case ENUMERATION:
|
||||
this.value=value.substring(1, value.length()-2);
|
||||
break;
|
||||
default:
|
||||
this.value=value;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -28,10 +28,6 @@ class TxAttributeType implements ITxAttributeType, Serializable {
|
|||
|
||||
private AssociationType type;
|
||||
|
||||
static ITxAttributeType getAttrType(String name, DataType dataType, AssociationType type){
|
||||
return TxAttributeTypeFactory.INSTANCE.getAttrType(name, dataType, type);
|
||||
}
|
||||
|
||||
TxAttributeType(String name, DataType dataType, AssociationType type){
|
||||
this.name=name;
|
||||
this.dataType=dataType;
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* 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.util.HashMap;
|
||||
|
||||
import com.minres.scviewer.database.AssociationType;
|
||||
import com.minres.scviewer.database.DataType;
|
||||
import com.minres.scviewer.database.tx.ITxAttributeType;
|
||||
|
||||
public class TxAttributeTypeFactory {
|
||||
public static final TxAttributeTypeFactory INSTANCE = new TxAttributeTypeFactory();
|
||||
|
||||
HashMap<String, ITxAttributeType> attributes = new HashMap<>();
|
||||
|
||||
private TxAttributeTypeFactory() {}
|
||||
|
||||
ITxAttributeType getAttrType(String name, DataType dataType, AssociationType type){
|
||||
String key = name+":"+dataType.toString();
|
||||
ITxAttributeType res;
|
||||
if(attributes.containsKey(key)){
|
||||
res=attributes.get(key);
|
||||
} else {
|
||||
res=new TxAttributeType(name, dataType, type);
|
||||
attributes.put(key, res);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
|
@ -1,34 +1,22 @@
|
|||
package com.minres.scviewer.database.text;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.minres.scviewer.database.EventKind;
|
||||
import com.minres.scviewer.database.WaveformType;
|
||||
import com.minres.scviewer.database.tx.ITx;
|
||||
import com.minres.scviewer.database.tx.ITxEvent;
|
||||
|
||||
class TxEvent implements ITxEvent, Serializable {
|
||||
class TxEvent implements ITxEvent {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 4481575593229248159L;
|
||||
final TextDbLoader loader;
|
||||
|
||||
final EventKind kind;
|
||||
|
||||
final ITx transaction;
|
||||
final Long transaction;
|
||||
|
||||
final Long time;
|
||||
|
||||
TxEvent(EventKind kind, ITx transaction) {
|
||||
super();
|
||||
this.kind = kind;
|
||||
this.transaction = transaction;
|
||||
this.time = kind==EventKind.BEGIN?transaction.getBeginTime():transaction.getEndTime();
|
||||
}
|
||||
|
||||
public TxEvent(EventKind kind, ITx transaction, Long time) {
|
||||
super();
|
||||
TxEvent(TextDbLoader loader, EventKind kind, Long transaction, Long time) {
|
||||
this.loader=loader;
|
||||
this.kind = kind;
|
||||
this.transaction = transaction;
|
||||
this.time = time;
|
||||
|
@ -37,13 +25,13 @@ class TxEvent implements ITxEvent, Serializable {
|
|||
@Override
|
||||
public
|
||||
ITxEvent duplicate() throws CloneNotSupportedException {
|
||||
return new TxEvent(kind, transaction, time);
|
||||
return new TxEvent(loader, kind, transaction, time);
|
||||
}
|
||||
|
||||
@Override
|
||||
public
|
||||
String toString() {
|
||||
return kind.toString()+"@"+time+" of tx #"+transaction.getId();
|
||||
return kind.toString()+"@"+time+" of tx #"+transaction;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -63,6 +51,6 @@ class TxEvent implements ITxEvent, Serializable {
|
|||
|
||||
@Override
|
||||
public ITx getTransaction() {
|
||||
return transaction;
|
||||
return loader.getTransaction(transaction);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,9 +35,9 @@ class TxGenerator implements ITxGenerator, Serializable{
|
|||
|
||||
ArrayList<ITx> transactions=new ArrayList<ITx>();
|
||||
|
||||
ArrayList<ITxAttributeType> beginAttrs = new ArrayList<ITxAttributeType>();
|
||||
ArrayList<TxAttributeType> beginAttrs = new ArrayList<>();
|
||||
|
||||
ArrayList<ITxAttributeType> endAttrs= new ArrayList<ITxAttributeType>();
|
||||
ArrayList<TxAttributeType> endAttrs= new ArrayList<>();
|
||||
|
||||
TxGenerator(Long id, TxStream stream, String name){
|
||||
this.id=id;
|
||||
|
@ -45,32 +45,34 @@ class TxGenerator implements ITxGenerator, Serializable{
|
|||
this.name=name;
|
||||
}
|
||||
|
||||
public IWaveform getStream(){
|
||||
return stream;
|
||||
}
|
||||
|
||||
public List<ITx> getTransactions(){
|
||||
return transactions;
|
||||
}
|
||||
|
||||
Boolean isActive() {return active;}
|
||||
|
||||
@Override
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IWaveform getStream(){
|
||||
return stream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public ArrayList<ITxAttributeType> getBeginAttrs() {
|
||||
@Override
|
||||
public List<ITx> getTransactions(){
|
||||
return transactions;
|
||||
}
|
||||
|
||||
public List<TxAttributeType> getBeginAttrs() {
|
||||
return beginAttrs;
|
||||
}
|
||||
|
||||
public ArrayList<ITxAttributeType> getEndAttrs() {
|
||||
public List<TxAttributeType> getEndAttrs() {
|
||||
return endAttrs;
|
||||
}
|
||||
|
||||
Boolean isActive() {return active;}
|
||||
|
||||
}
|
||||
|
|
|
@ -12,31 +12,28 @@ class TxRelation implements ITxRelation, Serializable {
|
|||
*/
|
||||
private static final long serialVersionUID = -347668857680574140L;
|
||||
|
||||
final Tx source;
|
||||
final TextDbLoader loader;
|
||||
|
||||
final Tx target;
|
||||
final ScvRelation scvRelation;
|
||||
|
||||
final RelationType relationType;
|
||||
|
||||
public TxRelation(RelationType relationType, Tx source, Tx target) {
|
||||
this.source = source;
|
||||
this.target = target;
|
||||
this.relationType = relationType;
|
||||
public TxRelation(TextDbLoader loader, ScvRelation scvRelation) {
|
||||
this.loader = loader;
|
||||
this.scvRelation = scvRelation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RelationType getRelationType() {
|
||||
return relationType;
|
||||
return scvRelation.relationType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITx getSource() {
|
||||
return source;
|
||||
return loader.getTransaction(scvRelation.source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITx getTarget() {
|
||||
return target;
|
||||
return loader.getTransaction(scvRelation.target);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -10,40 +10,32 @@
|
|||
*******************************************************************************/
|
||||
package com.minres.scviewer.database.text;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.NavigableMap;
|
||||
|
||||
import org.mapdb.BTreeMap;
|
||||
import org.mapdb.Serializer;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import com.minres.scviewer.database.EventKind;
|
||||
import com.minres.scviewer.database.HierNode;
|
||||
import com.minres.scviewer.database.IEvent;
|
||||
import com.minres.scviewer.database.IWaveform;
|
||||
import com.minres.scviewer.database.WaveformType;
|
||||
import com.minres.scviewer.database.tx.ITx;
|
||||
import com.minres.scviewer.database.tx.ITxEvent;
|
||||
import com.minres.scviewer.database.tx.ITxGenerator;
|
||||
|
||||
class TxStream extends HierNode implements IWaveform, Serializable {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 6721893017334753858L;
|
||||
class TxStream extends HierNode implements IWaveform {
|
||||
|
||||
private Long id;
|
||||
|
||||
private ArrayList<ITxGenerator> generators = new ArrayList<ITxGenerator>();
|
||||
private TextDbLoader loader;
|
||||
|
||||
private int maxConcurrency = 0;
|
||||
|
||||
private int concurrency = 0;
|
||||
|
||||
boolean concurrencyCalculated = false;
|
||||
|
||||
void setConcurrency(int concurrency) {
|
||||
this.concurrency = concurrency;
|
||||
if(concurrency>maxConcurrency)
|
||||
|
@ -54,19 +46,16 @@ class TxStream extends HierNode implements IWaveform, Serializable {
|
|||
return this.concurrency;
|
||||
}
|
||||
|
||||
private BTreeMap<Long, IEvent[]> events;
|
||||
TreeMap<Long, IEvent[]> events = new TreeMap<>();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
TxStream(TextDbLoader loader, Long id, String name, String kind){
|
||||
super(name);
|
||||
this.id=id;
|
||||
this.maxConcurrency=0;
|
||||
//events = new TreeMap<Long, List<ITxEvent>>()
|
||||
events = (BTreeMap<Long, IEvent[]>) loader.mapDb.treeMap(name).keySerializer(Serializer.LONG).createOrOpen();
|
||||
this.loader=loader;
|
||||
}
|
||||
|
||||
List<ITxGenerator> getGenerators(){
|
||||
return generators;
|
||||
return new ArrayList<>(loader.txGenerators.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -74,13 +63,26 @@ class TxStream extends HierNode implements IWaveform, Serializable {
|
|||
return maxConcurrency;
|
||||
}
|
||||
|
||||
public void addEvent(TxEvent evt) {
|
||||
if(!events.containsKey(evt.time))
|
||||
events.put(evt.time, new IEvent[] {evt});
|
||||
else {
|
||||
IEvent[] evts = events.get(evt.time);
|
||||
IEvent[] newEvts = Arrays.copyOf(evts, evts.length+1);
|
||||
newEvts[evts.length]=evt;
|
||||
events.put(evt.time, newEvts);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigableMap<Long, IEvent[]> getEvents() {
|
||||
if(!concurrencyCalculated) calculateConcurrency();
|
||||
return (NavigableMap<Long, IEvent[]>)events;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IEvent[] getEventsAtTime(Long time) {
|
||||
if(!concurrencyCalculated) calculateConcurrency();
|
||||
return events.get(time);
|
||||
}
|
||||
|
||||
|
@ -91,6 +93,7 @@ class TxStream extends HierNode implements IWaveform, Serializable {
|
|||
|
||||
@Override
|
||||
public IEvent[] getEventsBeforeTime(Long time) {
|
||||
if(!concurrencyCalculated) calculateConcurrency();
|
||||
Entry<Long, IEvent[]> e = events.floorEntry(time);
|
||||
if(e==null)
|
||||
return null;
|
||||
|
@ -108,4 +111,22 @@ class TxStream extends HierNode implements IWaveform, Serializable {
|
|||
return id;
|
||||
}
|
||||
|
||||
private void calculateConcurrency() {
|
||||
ArrayList<Long> rowendtime = new ArrayList<>();
|
||||
events.entrySet().stream().forEach( entry -> {
|
||||
IEvent[] values = entry.getValue();
|
||||
Arrays.asList(values).stream().filter(e->e.getKind()==EventKind.BEGIN).forEach(evt -> {
|
||||
Tx tx = (Tx) ((TxEvent)evt).getTransaction();
|
||||
int rowIdx = 0;
|
||||
for(; rowIdx<rowendtime.size() && rowendtime.get(rowIdx)>tx.getBeginTime(); rowIdx++);
|
||||
if(rowendtime.size()<=rowIdx)
|
||||
rowendtime.add(tx.getEndTime());
|
||||
else
|
||||
rowendtime.set(rowIdx, tx.getEndTime());
|
||||
tx.setConcurrencyIndex(rowIdx);
|
||||
});
|
||||
});
|
||||
concurrencyCalculated=true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -84,17 +84,20 @@ public class StreamPainter extends TrackPainter{
|
|||
NavigableMap<Long, IEvent[]> entries = stream.getEvents().subMap(firstTx.getKey(), true, lastTx.getKey(), true);
|
||||
boolean highlighed=false;
|
||||
proj.setForeground(this.waveCanvas.styleProvider.getColor(WaveformColors.LINE));
|
||||
|
||||
long selectedId=waveCanvas.currentSelection!=null? waveCanvas.currentSelection.getId():-1;
|
||||
for(Entry<Long, IEvent[]> entry: entries.entrySet())
|
||||
for(IEvent evt:entry.getValue()){
|
||||
ITxEvent txEvent = (ITxEvent) evt;
|
||||
if(txEvent.getKind()==EventKind.BEGIN)
|
||||
seenTx.add(txEvent.getTransaction());
|
||||
if(txEvent.getKind()==EventKind.END){
|
||||
ITx tx = txEvent.getTransaction();
|
||||
highlighed|=waveCanvas.currentSelection!=null && waveCanvas.currentSelection.equals(tx);
|
||||
drawTx(proj, area, tx, false);
|
||||
ITx tx = ((ITxEvent) evt).getTransaction();
|
||||
highlighed|=selectedId==tx.getId();
|
||||
switch(evt.getKind()) {
|
||||
case BEGIN:
|
||||
seenTx.add(tx);
|
||||
break;
|
||||
case END:
|
||||
seenTx.remove(tx);
|
||||
case SINGLE:
|
||||
drawTx(proj, area, tx, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for(ITx tx:seenTx){
|
||||
|
|
|
@ -178,13 +178,13 @@ public class WaveformView implements IWaveformView {
|
|||
@Override
|
||||
public void paintControl(PaintEvent e) {
|
||||
if(down) {
|
||||
GC gc = e.gc;
|
||||
gc.setAlpha(128);
|
||||
int minX = Math.min(start.x, end.x);
|
||||
int width = Math.max(start.x, end.x) - minX;
|
||||
int yTop = waveformCanvas.getRulerHeight();
|
||||
int yBottom = waveformCanvas.getSize().y;
|
||||
gc.fillRectangle(minX, yTop, width,yBottom);
|
||||
GC gc = e.gc;
|
||||
gc.setAlpha(128);
|
||||
int minX = Math.min(start.x, end.x);
|
||||
int width = Math.max(start.x, end.x) - minX;
|
||||
int yTop = waveformCanvas.getRulerHeight();
|
||||
int yBottom = waveformCanvas.getSize().y;
|
||||
gc.fillRectangle(minX, yTop, width,yBottom);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -478,10 +478,10 @@ public class WaveformView implements IWaveformView {
|
|||
if ("size".equals(pce.getPropertyName()) || "content".equals(pce.getPropertyName())) {
|
||||
if(revealSelected) {
|
||||
waveformCanvas.getDisplay().asyncExec(() ->{
|
||||
update();
|
||||
currentWaveformSelection.stream().forEach(e -> waveformCanvas.reveal(e.waveform));
|
||||
valueList.redraw();
|
||||
nameList.redraw();
|
||||
update();
|
||||
currentWaveformSelection.stream().forEach(e -> waveformCanvas.reveal(e.waveform));
|
||||
valueList.redraw();
|
||||
nameList.redraw();
|
||||
});
|
||||
revealSelected=false;
|
||||
} else
|
||||
|
@ -574,11 +574,11 @@ public class WaveformView implements IWaveformView {
|
|||
do {
|
||||
for(IEvent evt:firstTx.getValue()){
|
||||
if(evt instanceof ITxEvent) {
|
||||
ITx tx=((ITxEvent)evt).getTransaction();
|
||||
if(evt.getKind()==EventKind.BEGIN && tx.getBeginTime()<=time && tx.getEndTime()>=time){
|
||||
if(resultsList[tx.getConcurrencyIndex()]==null)
|
||||
resultsList[tx.getConcurrencyIndex()]= ((ITxEvent)evt).getTransaction();
|
||||
}
|
||||
ITx tx=((ITxEvent)evt).getTransaction();
|
||||
if(evt.getKind()==EventKind.BEGIN && tx.getBeginTime()<=time && tx.getEndTime()>=time){
|
||||
if(resultsList[tx.getConcurrencyIndex()]==null)
|
||||
resultsList[tx.getConcurrencyIndex()]= ((ITxEvent)evt).getTransaction();
|
||||
}
|
||||
}
|
||||
}
|
||||
firstTx=entry.waveform.getEvents().lowerEntry(firstTx.getKey());
|
||||
|
@ -754,12 +754,12 @@ public class WaveformView implements IWaveformView {
|
|||
moveSelection(direction, NEXT_PREV_IN_STREAM) ;
|
||||
else {
|
||||
if(currentWaveformSelection.size()==1) {
|
||||
int idx = streams.indexOf(currentWaveformSelection.get(0));
|
||||
if(direction==GotoDirection.UP && idx>0) {
|
||||
setSelection(new StructuredSelection(streams.get(idx-1)));
|
||||
} else if(direction==GotoDirection.DOWN && idx<(streams.size()-1)) {
|
||||
setSelection(new StructuredSelection(streams.get(idx+1)));
|
||||
}
|
||||
int idx = streams.indexOf(currentWaveformSelection.get(0));
|
||||
if(direction==GotoDirection.UP && idx>0) {
|
||||
setSelection(new StructuredSelection(streams.get(idx-1)));
|
||||
} else if(direction==GotoDirection.DOWN && idx<(streams.size()-1)) {
|
||||
setSelection(new StructuredSelection(streams.get(idx+1)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1201,15 +1201,15 @@ public class WaveformView implements IWaveformView {
|
|||
}
|
||||
}
|
||||
});
|
||||
// int style = SWT.MULTI | SWT.WRAP | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER;
|
||||
// final StyledText text = new StyledText(waveformCanvas, style);
|
||||
// text.setText("Dragging");
|
||||
// dragSource.setDragSourceEffect(new DragSourceEffect(text) {
|
||||
// @Override
|
||||
// public void dragStart(DragSourceEvent event) {
|
||||
// event.image = waveformCanvas.getDisplay().getSystemImage(SWT.ICON_WARNING);
|
||||
// }
|
||||
// });
|
||||
// int style = SWT.MULTI | SWT.WRAP | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER;
|
||||
// final StyledText text = new StyledText(waveformCanvas, style);
|
||||
// text.setText("Dragging");
|
||||
// dragSource.setDragSourceEffect(new DragSourceEffect(text) {
|
||||
// @Override
|
||||
// public void dragStart(DragSourceEvent event) {
|
||||
// event.image = waveformCanvas.getDisplay().getSystemImage(SWT.ICON_WARNING);
|
||||
// }
|
||||
// });
|
||||
}
|
||||
|
||||
private void createWaveformDropTarget(final Canvas canvas) {
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
*******************************************************************************/
|
||||
package com.minres.scviewer.database.tx;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.minres.scviewer.database.AssociationType;
|
||||
import com.minres.scviewer.database.DataType;
|
||||
|
||||
|
|
|
@ -93,8 +93,18 @@ public class DatabaseServicesTest {
|
|||
assertTrue(f.exists());
|
||||
waveformDb.load(f);
|
||||
assertNotNull(waveformDb);
|
||||
assertEquals(3, waveformDb.getAllWaves().size());
|
||||
List<IWaveform> waveforms = waveformDb.getAllWaves();
|
||||
assertEquals(3, waveforms.size());
|
||||
assertEquals(1, waveformDb.getChildNodes().size());
|
||||
for(IWaveform w:waveforms) {
|
||||
if(w.getId().equals(1l)) {
|
||||
assertEquals(2, w.getWidth());
|
||||
} else if(w.getId().equals(2l)) {
|
||||
assertEquals(1, w.getWidth());
|
||||
} else if(w.getId().equals(3l)) {
|
||||
assertEquals(1, w.getWidth());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
Loading…
Reference in New Issue