io.github.contractautomata.catlib.converters.AutDataConverter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of catlib Show documentation
Show all versions of catlib Show documentation
Library for specifying and verifying contract automata.
The newest version!
package io.github.contractautomata.catlib.converters;
import com.google.re2j.Matcher;
import com.google.re2j.Pattern;
import io.github.contractautomata.catlib.automaton.Automaton;
import io.github.contractautomata.catlib.automaton.label.Label;
import io.github.contractautomata.catlib.automaton.label.action.Action;
import io.github.contractautomata.catlib.automaton.state.BasicState;
import io.github.contractautomata.catlib.automaton.state.State;
import io.github.contractautomata.catlib.automaton.transition.ModalTransition;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
/**
* This class supports the conversion of an automaton into a textual format, with extension .data
.
*
* @param the type of the label of the automaton to import, must extend Label<Action>
*
* @author Davide Basile
*/
public class AutDataConverter> implements AutConverter, ModalTransition,L>>,Automaton,?,?,?>> {
/**
* a builder of a label of type L from a list of actions
*/
private final Function,L> createLabel;
/**
* suffix, the used file extension
*/
private static final String SUFFIX = ".data";
/**
* message to show in case of an empty file name
*/
private static final String EMPTYMSG = "Empty file name";
/**
* Constructor.
* @param createLabel a function building a label from a list of actions, used during import when instantiating the labels of the automaton
*/
public AutDataConverter(Function, L> createLabel) {
super();
this.createLabel = createLabel;
}
/**
* Import an automaton from a textual representation
* @param filename the name of the file containing the automaton
* @return the imported automaton, where the content of each state and action is a String, labels are of type L, and transitions can have modalities
* @throws IOException
*/
public Automaton,ModalTransition,L>> importMSCA(String filename) throws IOException {
if (!filename.endsWith(SUFFIX))
throw new IllegalArgumentException("Not a .data format");
Path path = FileSystems.getDefault().getPath(filename);
String safeFileName = path.toString();
Set,L>> tr;
//https://github.com/find-sec-bugs/find-sec-bugs/issues/241
try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(safeFileName), StandardCharsets.UTF_8)))
{
int rank=0;
String[] initial = new String[1];
String[][] fin = new String[1][];
tr = new HashSet<>();
Set> states = new HashSet<>();
Map>> mapBasicStates = new HashMap<>();
String strLine;
while ((strLine = br.readLine()) != null)
{
if (strLine.length()>0)
{
String subStrLine=strLine.substring(0,1);
switch(subStrLine)
{
case "R": //Rank Line
{
rank=Integer.parseInt(strLine.substring(6));
break;
}
case "I": //Initial state
{
initial=readInitialState(strLine,rank);
break;
}
case "F": //Final state
{
fin=readFinalState(strLine,rank);
break;
}
case "(": //a permitted transition
{
tr.add(loadTransition(strLine,rank, ModalTransition.Modality.PERMITTED, states,mapBasicStates,initial,fin));
break;
}
case ModalTransition.NECESSARY: //a necessary transition
{
tr.add(loadTransition(strLine,rank,readModality(strLine),states,mapBasicStates,initial,fin));
break;
}
default :
}
}
}
}
return new Automaton<>(tr);
}
private String[] readInitialState(String strLine, int rank){
String[] initial=Arrays.stream(strLine.split("[\\[\\],]"))
.filter(s->!s.contains("Initial state"))
.map(String::trim)
.toArray(String[]::new);
if (initial.length!=rank)
throw new IllegalArgumentException("Initial state with different rank");
return initial;
}
private String[][] readFinalState(String strLine, int rank){
String[][] fin=Arrays.stream(strLine.split("]"))
.map(sar->Arrays.stream(sar.split("[,|\\[]"))
.filter(s->!s.contains("Final states"))
.map(String::trim)
.filter(s->!s.isEmpty())
.toArray(String[]::new))
.toArray(String[][]::new);
if (fin.length!=rank)
throw new IllegalArgumentException("Final states with different rank");
return fin;
}
private ModalTransition.Modality readModality(String strLine) {
String stype= strLine.substring(1,2);
if (ModalTransition.URGENT.equals(stype))
return ModalTransition.Modality.URGENT;
else if (ModalTransition.LAZY.equals(stype))
return ModalTransition.Modality.LAZY;
else
throw new IllegalArgumentException("Invalid modality");
}
private ModalTransition,L> loadTransition(String str, int rank, ModalTransition.Modality type, Set> states,Map>> mapBasicStates,String[] initial, String[][] fin) throws IOException
{
String regex = "\\(\\["+"(.+)"+"\\],\\["+"(.+)"+"\\],\\["+"(.+)"+"\\]\\)";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(str);
matcher.find();
String[][] tr=IntStream.range(1,4)
.mapToObj(i->Arrays.stream(matcher.group(i).split(","))
.map(String::trim)
.toArray(String[]::new))
.toArray(String[][]::new);
if (tr[0].length!=rank ||
tr[1].length!=rank ||
tr[2].length!=rank)
throw new IOException("Ill-formed transitions, different ranks");
State source = createOrLoadState(states,mapBasicStates,tr[0],initial, fin);//source
State target = createOrLoadState(states,mapBasicStates,tr[2],initial, fin);//target
return new ModalTransition<>(source,createLabel(tr),target,type);
}
private L createLabel(String[][] tr) {
try { return createLabel.apply(Arrays.stream(tr[1]).map(this::parseAction).collect(Collectors.toList()));}
catch (IllegalArgumentException e) {
//parsing failed
return createLabel.apply(Arrays.stream(tr[1])
.map(Action::new)
.collect(Collectors.toList()));
}
}
private State createOrLoadState(Set> states,Map>> mapBasicStates, String[] state,String[] initial, String[][] fin) {
return states.stream()
.filter(cs->IntStream.range(0, cs.getState().size())
.allMatch(i->cs.getState().get(i).getState().equals(state[i])))
.findAny()
.orElseGet(()->{
State temp= new State<>(
IntStream.range(0, state.length) //creating the list of basic states using mapBasicStates
.mapToObj(i->{
Set> l = mapBasicStates.get(i);
if (l==null || l.stream().noneMatch(bs-> bs.getState().equals(state[i])))
{
BasicState bs=new BasicState<>(state[i]+"",
state[i].equals(initial[i]),
Arrays.stream(fin[i]).anyMatch(id->id.equals(state[i])));
if (l==null)
mapBasicStates.put(i, new HashSet<>(List.of(bs)));
else
l.add(bs);
return bs;
} else
return l.stream()
.filter(bs->bs.getState().equals(state[i]))
.findFirst()
.orElseThrow(RuntimeException::new);
}).collect(Collectors.toList()));
states.add(temp); return temp;});
}
/**
* Store the automaton passed as argument in a .data
format.
*
* @param filename the name of the file to store
* @param aut the automaton to store
* @throws IOException
*/
@Override
public void exportMSCA(String filename, Automaton,?,?,?> aut) throws IOException {
if (filename.isEmpty())
throw new IllegalArgumentException(EMPTYMSG);
String ext=(filename.endsWith(SUFFIX))?"":SUFFIX;
Path path = FileSystems.getDefault().getPath(filename+ext);
String safeFileName = path.toString();
try (PrintWriter pr = new PrintWriter(new OutputStreamWriter(new FileOutputStream(new File(safeFileName)), StandardCharsets.UTF_8)))
{
pr.print(aut.toString());
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy