package is.L42.connected.withSafeOperators.pluginWrapper;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.Map.Entry;
import ast.Ast;
import ast.Ast.Doc;
import ast.Ast.Mdf;
import ast.Ast.MethodSelector;
import ast.Ast.MethodType;
import ast.Ast.NormType;
import ast.Ast.Path;
import ast.Ast.Type;
import ast.ExpCore;
import ast.ExpCore.Block;
import ast.ExpCore.Block.Dec;
import ast.ExpCore.Block.On;
import ast.ExpCore.ClassB;
import ast.ExpCore.ClassB.Member;
import ast.ExpCore.ClassB.MethodWithType;
import ast.ExpCore.ClassB.NestedClass;
import ast.ExpCore.MCall;
import ast.ExpCore.Signal;
import ast.ExpCore.Using;
import ast.Expression;
import auxiliaryGrammar.EncodingHelper;
import auxiliaryGrammar.Functions;
import programReduction.Program;
import coreVisitors.From;
import facade.L42;
import facade.Parser;
import is.L42.connected.withSafeOperators.pluginWrapper.RefactorErrors.ClassUnfit;
import is.L42.connected.withSafeOperators.pluginWrapper.RefactorErrors.MethodUnfit;
import is.L42.connected.withSafeOperators.pluginWrapper.RefactorErrors.UnresolvedOverloading;
import facade.L42.ExecutionStage;
import facade.PData;
import platformSpecific.fakeInternet.OnLineCode;
import platformSpecific.fakeInternet.PluginWithPart;
import platformSpecific.fakeInternet.PluginWithPart.PlgInfo;
import platformSpecific.fakeInternet.PluginWithPart.UsingInfo;
import sugarVisitors.Desugar;
import sugarVisitors.InjectionOnCore;
import tools.Assertions;
public class PlgWrapperGenerator {
private static List<ClassB.Member> templateWrapper=((ClassB)parseAndDesugar(
" {class method\n"+
" mut This0 #from(Library binaryRepr)\n"+
" read method\n"+
" Library #binaryRepr()\n"+
" class method\n"+
" Void #exceptionIf(Library binaryRepr) exception This\n"+
" use This check instanceof(_this:binaryRepr)\n"+
" exception This.#from(binaryRepr:binaryRepr)\n"+
"}")).getMs();
private static MCall templateUsingExc=(MCall) ((ClassB)parseAndDesugar(
" {class method This m()\n"+
" This.#from(binaryRepr:(\n"+
" Library res=use This0 check m(_:This.#binaryRepr())\n"+
" error This.#pluginUnresponsive(binaryRepr:void)\n"+
" catch error Library x (\n"+
" This.#exceptionIf(binaryRepr:x)\n"+
" error x\n"+
" )\n"+
" res\n"+
" ))\n"+
"}\n"+
"")).getMs().get(0).getInner();
private static ExpCore parseAndDesugar(String s) {
Expression code1=Parser.parse("PlgWrapperGenerator",s);
Expression code2=Desugar.of(code1);
return code2.accept(new InjectionOnCore());
}
//-----------------------------------------------------
public static ClassB main(PData data,ClassB l) throws UnresolvedOverloading, ClassUnfit, MethodUnfit{
return plgComplete(data.p.evilPush(l));
}
public static ClassB plgComplete(Program p) throws UnresolvedOverloading, ClassUnfit, MethodUnfit{//p contains the l to modify at top
return plgComplete(Collections.emptyList(),p,p.top());
}
public static ClassB plgComplete(List<Ast.C>cs,Program p,ClassB l) throws UnresolvedOverloading, ClassUnfit, MethodUnfit{
//p.top() is topL
List<Member> ms=new ArrayList<>();
for(Member m: l.getMs()){
if(!(m instanceof NestedClass)){
ms.add(m);
continue;
}
NestedClass nc=(NestedClass)m;
List<Ast.C>csc=new ArrayList<>(cs);
csc.add(nc.getName());
ms.add(nc.withInner(
plgComplete(csc,p,(ClassB)nc.getInner()))
);
}
return plgComplete1(cs,p,l.withMs(ms));
//forall nested c in l
// l=l[with c=plgComplete(cs:c, p,l.c)
//return plgComplete1(cs,p,l)
}
public static int argLessPData(Method jm){
Class<?>[]ps=jm.getParameterTypes();
if(ps.length==0){return 0;}
if(ps[0].equals(PData.class)){return ps.length-1;}
return ps.length;
//TODO:?assert others are not PData?
}
public static int argLessPData(Constructor<?> jc){
Class<?>[]ps=jc.getParameterTypes();
if(ps.length==0){return 0;}
if(ps[0].equals(PData.class)){return ps.length-1;}
return ps.length;
//TODO:?assert others are not PData?
}
public static ClassB plgComplete1(List<Ast.C>cs,Program p,ClassB l) throws UnresolvedOverloading, ClassUnfit, MethodUnfit{
PluginWithPart pwp = OnLineCode._isPluginWithPart(l.getDoc1());
if(pwp==null){return l;}
PlgInfo plgInfo=new PlgInfo(l.getDoc1());
if(!hasPluginUnresponsive(l)){
throw new RefactorErrors.ClassUnfit().msg(
"Class "+Path.outer(0,cs) +" does not contain method #pluginUnresponsive(binaryRepr)");
}
Class<?> c=pwp.pointed;
Method[] jms=c.getMethods();
Constructor<?>[] jcs=c.getDeclaredConstructors();
List<Member>msResult=new ArrayList<>(templateWrapper);
Path pTop=Path.outer(0, cs);
for(Member m:l.getMs()){
if (!(m instanceof MethodWithType)){
msResult.add(m);
continue;
}
MethodWithType mwt=(MethodWithType)m;
if (mwt.get_inner().isPresent()){
msResult.add(mwt);
continue;
}
addMwt(p, plgInfo, jms,jcs, msResult, pTop, mwt);
}
return l.withMs(msResult);
}
private static void addMwt(Program p, PlgInfo plgInfo, Method[] jms, Constructor<?>[] jcs, List<Member> msResult, Path pTop, MethodWithType mwt) throws UnresolvedOverloading, ClassUnfit, MethodUnfit {
//checks
try{
isOkAsReturn(p,pTop,mwt.getMt().getReturnType());
for(Type ti:mwt.getMt().getExceptions()){
isOkAsException(p,pTop,ti.getNT().getPath());
}
for(Type ti:mwt.getMt().getTs()){
isOkAsParameter(p,pTop,ti);
}//TODO: we may want to cache those tests if performance is needed
}
catch(ClassUnfit| MethodUnfit e){
e.setMessage("While examining Class "+pTop+" method "+mwt.getMs()+":\n"+e.getMessage());
throw e;
}
//add to msResult
//TODO: add behaviour if mwt have special comment to define specific ms for use
String name=mwt.getMs().nameToS();
if(name.startsWith("#")){name=name.substring(1);}
UsingInfo ui;
if (!name.equals("new") && !name.equals("apply")) ui= usingMethod(plgInfo, jms, mwt, name);
else ui=usingConstructor(plgInfo, jcs, mwt, name);
MethodWithType tu=updateTemplateUsing(ui,mwt);
msResult.add(tu);
}
private static UsingInfo usingMethod(PlgInfo plgInfo, Method[] jms, MethodWithType mwt, String name) throws UnresolvedOverloading {
List<Method>jms0=new ArrayList<>();
for (Method mi:jms){
if (!mi.getName().equals(name)){continue;}
if (argLessPData(mi)!=mwt.getMs().getNames().size()){continue;}
jms0.add(mi);
}
if (jms0.size()!=1){
throw new RefactorErrors.UnresolvedOverloading().msg(
"The referred java class "+plgInfo.plgClass.getName()+
" does not have exactly one method for "+name+" with right number of arguments.\n"+
"List of candidate methods:"+jms0);
}
assert jms0.size()==1:
jms0.size();
Method jm=jms0.get(0);
UsingInfo ui=new UsingInfo(plgInfo,jm);
return ui;
}
private static UsingInfo usingConstructor(PlgInfo plgInfo, Constructor<?>[] jcs, MethodWithType mwt, String name) throws UnresolvedOverloading {
List<Constructor<?>>jcs0=new ArrayList<>();
for (Constructor<?> mi:jcs){
if (argLessPData(mi)!=mwt.getMs().getNames().size()){continue;}
jcs0.add(mi);
}
if (jcs0.size()!=1){
throw new RefactorErrors.UnresolvedOverloading().msg(
"The referred java class "+plgInfo.plgClass.getName()+
" does not have exactly one constructor with right number of arguments.\n"+
"List of candidate constructors:"+jcs0);
}
assert jcs0.size()==1:
jcs0.size();
Constructor<?> jc=jcs0.get(0);
UsingInfo ui=new UsingInfo(plgInfo,jc);
return ui;
}
private static MethodWithType updateTemplateUsing(UsingInfo ui, MethodWithType mwt) {
MCall e=templateUsingExc;
MethodType mt=mwt.getMt();
ExpCore.Block b=(Block) e.getEs().get(0);
if (mwt.getMt().getExceptions().isEmpty()){b=b.withOns(Collections.emptyList());}
ExpCore.Using u=(Using) b.getDecs().get(0).getInner();
ExpCore.MCall p0=(MCall) u.getEs().get(0);//parameter expressions
//e#mcall.inner<-mwt.retType.path
e=e.withInner(ExpCore.EPath.wrap(mt.getReturnType().getNT().getPath()));
//u=u.withS(ui.usingMs);
List<ExpCore> ues=new ArrayList<>();
if(mt.getMdf()!=Mdf.Class){
ues.add(p0.withInner(new ExpCore.X(mwt.getP(),"this")));
}
{int i=-1;for(String x: mwt.getMs().getNames()){i++;
ExpCore pi=new ExpCore.X(mwt.getP(),x);
boolean needAddBinaryRepr=true;
Type ti=mwt.getMt().getTs().get(i);
if(ti.equals(NormType.immLibrary)){
needAddBinaryRepr=false;
}
if(ti.getNT().getMdf()==Mdf.Class){
needAddBinaryRepr=false;
}
if(needAddBinaryRepr){
pi=p0.withInner(pi);
}
ues.add(pi);
}}
u=new Using(u.getPath(),ui.usingMs,u.getDoc(),ues,u.getInner());
String errorS=
"plugin string: "+ui.plgInfo.plgString+"\n"+
"plugin part: "+ui.plgInfo.plgName +"\n"+
"method name: "+mwt.getMs() +"\n"+
"java method: "+ui.plgInfo.plgClass.getName()+"."+ui.usingMs +"\n";
//u.inner#mcall.es(0)<-EncodingHelper.wrapStringU()
List<ExpCore> errorEs=Collections.singletonList(EncodingHelper.wrapStringU(errorS));
Signal tmpS=(Signal)u.getInner();
MCall tmpMc=((MCall)tmpS.getInner()).withEs(errorEs);
u=u.withInner(tmpS.withInner(tmpMc));
b=b.withDecs(Collections.singletonList(b.getDecs().get(0).withInner(u)));
//--
if (!mwt.getMt().getExceptions().isEmpty()){
On on=b.getOns().get(0);
Dec k0 = ((Block)on.getInner()).getDecs().get(0);
List<Dec> ks=new ArrayList<>();
{int i=-1;for(Type ti:mt.getExceptions()){i++;
MCall mci=((MCall)k0.getInner()).withInner(ExpCore.EPath.wrap(ti.getNT().getPath()));
ks.add(k0.withInner(mci).withX(k0.getX()+i));
}}
on=on.withInner(((Block)on.getInner()).withDecs(ks));
b=b.withOns(Collections.singletonList(on));
//k0=b.k(0).inner#block.decs(0)
//k0 add more on need
//ki.inner#mcall.inner<-Pi
}
if (ui.isVoid){b=b.withDecs(Collections.singletonList(b.getDecs().get(0).withT(NormType.immVoid)));}
if(!ui.isVoid && !mwt.getMt().getReturnType().equals(NormType.immLibrary)){
e=e.withEs(Collections.singletonList(b));
mwt=mwt.withInner(e);
}
else{
mwt=mwt.withInner(b);
}
return mwt;
}
public static boolean hasPluginUnresponsive(ClassB l){
//class method T #pluginUnresponsive(Library binaryRepr)
MethodSelector ms=MethodSelector.of("#pluginUnresponsive",Collections.singletonList("binaryRepr"));
MethodWithType mwt=(MethodWithType)l._getMember(ms);
if(mwt==null){return false;}//must be an mwt since normalized
MethodType mt=mwt.getMt();
if(!mt.getMdf().equals(Mdf.Class)){return false;}
if(!mt.getTs().get(0).equals(NormType.immLibrary)){return false;}
if(!mt.getExceptions().isEmpty()){return false;}
if(!mt.getReturnType().getNT().getMdf().equals(Mdf.Immutable)){return false;}
return true;//no need to check return type, since is just thrown as error
}
public static boolean hasBinaryRepr(ClassB l){
//read method Library #binaryRepr()
MethodSelector ms=MethodSelector.of("#binaryRepr",Collections.emptyList());
MethodWithType mwt=(MethodWithType)l._getMember(ms);
if(mwt==null){return false;}//must be an mwt since normalized
MethodType mt=mwt.getMt();
if(!mt.getMdf().equals(Mdf.Readable)){return false;}
if(!mt.getTs().isEmpty()){return false;}
if(!mt.getExceptions().isEmpty()){return false;}
if(!mt.getReturnType().equals(NormType.immLibrary)){return false;}
return true;
}
public static boolean hasFrom(ClassB l){
// class method mut This0 #from(Library binaryRepr)
MethodSelector ms=MethodSelector.of("#from",Collections.singletonList("binaryRepr"));
MethodWithType mwt=(MethodWithType)l._getMember(ms);
if(mwt==null){return false;}//must be an mwt since normalized
MethodType mt=mwt.getMt();
if(!mt.getMdf().equals(Mdf.Class)){return false;}
if(!mt.getTs().get(0).equals(NormType.immLibrary)){return false;}
if(!mt.getExceptions().isEmpty()){return false;}
if(!mt.getReturnType().equals(NormType.mutThis0)){return false;}
return true;
}
public static boolean hasExceptionIf(ClassB l){
//class method Void #exceptionIf(Library binaryRepr) exception This
MethodSelector ms= MethodSelector.of("#exceptionIf",Collections.singletonList("binaryRepr"));
MethodWithType mwt=(MethodWithType)l._getMember(ms);
if(mwt==null){return false;}//must be an mwt since normalized
MethodType mt=mwt.getMt();
if(!mt.getMdf().equals(Mdf.Class)){return false;}
if(!mt.getTs().get(0).equals(NormType.immLibrary)){return false;}
if(mt.getExceptions().size()!=1){return false;}
if(!mt.getExceptions().get(0).equals(Path.outer(0))){return false;}
if(!mt.getReturnType().equals(NormType.immVoid)){return false;}
return true;//no need to check return type, since is just thrown as error
}
private static Path _pathForOutside(int dept,Path pi){
if(pi.isPrimitive()){return null;}
if(pi.outerNumber()<=dept){return null;}
return pi.setNewOuter(pi.outerNumber()-dept);
}
private static void checkForInside(ClassB lTop,Path csTop,Path originalPath) throws ClassUnfit, MethodUnfit{
if(originalPath.isPrimitive()){
throw new RefactorErrors.MethodUnfit().msg(
"Method signature not supported:\n parameters can not be Void/Any/fwd.\n"
+ "returns can not be Any/fwd\n"
+ "exceptions can not be Any/Void/Library");
}
Path cs=From.fromP(originalPath,csTop);
assert cs.outerNumber()==0;
List<Ast.C> cBar = cs.getCBar();
ClassB lPointed=lTop.getClassB(cBar);
Doc d=lPointed.getDoc1();
if(d._getParameterForPlugin()==null ||d._getParameterForPluginPart()==null){
throw new RefactorErrors.ClassUnfit().msg(
"Class "+cBar+" doesnot have @pluginPart annotation");
}
}
private static void isOkAsParameter(Program p, Path csTop, Type ti) throws ClassUnfit, MethodUnfit {
Path pi=ti.getNT().getPath();
if(ti.getNT().getMdf()==Mdf.Class){return;}//class parameters are ok, and we just omit the .#binaryRepr() call
if (pi.equals(Path.Library())){return;}//Libraries are ok and we just omit the .#binaryRepr() call
Path op=_pathForOutside(csTop.getCBar().size(),pi);
if(op==null){checkForInside(p.top(),csTop,pi); return;}
ClassB l=p.extractClassB(op);//TODO: since p.top is topL, is it ok this extraction?
boolean hasIt=hasBinaryRepr(l);
boolean phOk=Functions.isComplete(ti.getNT());
if(!hasIt){throw new RefactorErrors.ClassUnfit().msg(
"Class "+op+" has no #binaryRepr() method");
}if(!phOk){throw new RefactorErrors.MethodUnfit().msg(
"Fwd types not allowed.");
}
}
private static void isOkAsException(Program p, Path csTop, Path pi) throws ClassUnfit, MethodUnfit {
Path op=_pathForOutside(csTop.getCBar().size(),pi);
if(op==null){checkForInside(p.top(),csTop,pi);return;}
ClassB l=p.extractClassB(op);
if(!hasExceptionIf(l)){throw new RefactorErrors.ClassUnfit().msg(
"Class "+op+" has no method #exceptionIf(binaryRepr)");
}
}
private static void isOkAsReturn(Program p, Path csTop, Type ti) throws ClassUnfit, MethodUnfit {
Path pi=ti.getNT().getPath();
if (pi.equals(Path.Void())){return;}
if (pi.equals(Path.Library())){return;}//We will need to generate a simpler returning expression
Path op=_pathForOutside(csTop.getCBar().size(),pi);
if(op==null){checkForInside(p.top(),csTop,pi); return;}
ClassB l=p.extractClassB(op);
boolean hasIt=hasFrom(l);
boolean phOk=Functions.isComplete(ti.getNT());
if (ti.getNT().getMdf()==Mdf.Class && !ti.equals(NormType.classAny)){
throw new RefactorErrors.MethodUnfit().msg("Return type can be 'class' only if is exactly 'class any'");
}
if(!hasIt){throw new RefactorErrors.ClassUnfit().msg(
"Class "+op+" has no method #from(binaryRepr)");
}
if(!phOk){//TODO: why this limitation?
throw new RefactorErrors.MethodUnfit().msg("Return type can not be fwd");
}
}
}
/*
invalidMethodType
where: Path, ms
what: invalid ret/par/exc type
why: no *primitive par/ret/exc void
method *123 not present in external ref
plg part not internal ref
no class/fwd par/ret
no plgunresponsive
where: Path, ms
overloading
where: Path, ms
in JavaClass selector/num have x variations/ is not present
LocationIssue
Any Operator cause //this will store some of the parameters of the operation
Library originalInput
internal/ext
Path /ClassAny
opt ms
opt sublocation (0 ret 1..n pars -1..-k exceptions, this??)
size code
issue string
LocationIssue next?
General exceptions for Refactor
SelectorNotFound
PathNotFound
MethodClash //2 methods not ok together
MethodUnfit //1 method not ok shape
ClassClash
ClassUnfit
privacycoupuled
incoherentRedirectMapping
cicular interface implements induced
overloading
error may talk about extern
have field internalLocation to talk about why pointing out
*/
//TODO: if a class is internal and is not plugin with part, is it ok if it has the right methods anyway??