| 1 | package uk.co.zonetora.fj.model; |
| 2 | |
| 3 | import static uk.co.zonetora.fj.util.ListUtil.mapSnd; |
| 4 | |
| 5 | import java.util.ArrayList; |
| 6 | import java.util.HashSet; |
| 7 | import java.util.List; |
| 8 | import java.util.Set; |
| 9 | |
| 10 | import uk.co.zonetora.fj.passes.FJException; |
| 11 | import uk.co.zonetora.fj.typecheck.ClassTable; |
| 12 | import uk.co.zonetora.fj.util.Just; |
| 13 | import uk.co.zonetora.fj.util.Maybe; |
| 14 | import uk.co.zonetora.fj.util.Tuple; |
| 15 | |
| 16 | public class ClassDecl { |
| 17 | |
| 18 | private final ClassName className; |
| 19 | private final ClassName superClass; |
| 20 | |
| 21 | private final List<Tuple<ClassName, FieldName>> fields; |
| 22 | |
| 23 | private final Constructor constructor; |
| 24 | |
| 25 | private final List<Method> methods; |
| 26 | |
| 27 | public ClassDecl(ClassName className, ClassName superClass, |
| 28 | Constructor constructor) { |
| 29 | this.className = className; |
| 30 | this.superClass = superClass; |
| 31 | this.fields = new ArrayList<Tuple<ClassName,FieldName>>(); |
| 32 | this.constructor = constructor; |
| 33 | this.methods = new ArrayList<Method>(); |
| 34 | } |
| 35 | |
| 36 | public void addField(ClassName cn, FieldName fn) { |
| 37 | this.fields.add(new Tuple<ClassName,FieldName>(cn,fn)); |
| 38 | } |
| 39 | |
| 40 | public void addMethod(Method m) { |
| 41 | this.methods.add(m); |
| 42 | } |
| 43 | |
| 44 | public ClassName getClassName() { |
| 45 | return this.className; |
| 46 | } |
| 47 | |
| 48 | public ClassName getSuperClass() { |
| 49 | return this.superClass; |
| 50 | } |
| 51 | |
| 52 | public Set<ClassName> getAllReferencedClassNames() { |
| 53 | Set<ClassName> referencedClassNames = new HashSet<ClassName>(); |
| 54 | referencedClassNames.add(className); |
| 55 | referencedClassNames.add(superClass); |
| 56 | for(Tuple<ClassName, FieldName> t : fields) { |
| 57 | referencedClassNames.add(t.getX()); |
| 58 | } |
| 59 | referencedClassNames.addAll(constructor.getAllReferencedClassNames()); |
| 60 | for(Method m : methods) { |
| 61 | referencedClassNames.addAll(m.getAllReferencedClassNames()); |
| 62 | } |
| 63 | return referencedClassNames; |
| 64 | } |
| 65 | |
| 66 | public List<FJException> checkCOK(ClassTable c) { |
| 67 | List<FJException> exceptions = new ArrayList<FJException>(); |
| 68 | |
| 69 | List<Tuple<ClassName, FieldName>> superFields = c.fields(superClass); |
| 70 | |
| 71 | try{ |
| 72 | checkNoFieldNameShadowing(superFields); |
| 73 | checkConstructorReturnTypeMatchesUp(); |
| 74 | checkConstructorHasGoodArguments(c); |
| 75 | } catch(FJException e) { |
| 76 | exceptions.add(e); |
| 77 | return exceptions; |
| 78 | } |
| 79 | |
| 80 | for(Method m : this.methods) { |
| 81 | exceptions.addAll(m.checkMOk(this, c)); |
| 82 | } |
| 83 | |
| 84 | |
| 85 | return exceptions; |
| 86 | } |
| 87 | |
| 88 | private void checkConstructorReturnTypeMatchesUp() throws FJException { |
| 89 | if(!constructor.getReturnClassName().equals(className)) { |
| 90 | throw new FJException("Constructor has wrong return type"); |
| 91 | } |
| 92 | } |
| 93 | |
| 94 | private void checkConstructorHasGoodArguments(ClassTable c) throws FJException { |
| 95 | List<Tuple<ClassName, FieldName>> superFields = c.fields(superClass); |
| 96 | |
| 97 | constructor.checkFieldsAreSane(fields, superFields); |
| 98 | } |
| 99 | |
| 100 | private void checkNoFieldNameShadowing(List<Tuple<ClassName, FieldName>> superFields) throws FJException { |
| 101 | Set<FieldName> superNames = new HashSet<FieldName>(mapSnd(superFields)); |
| 102 | Set<FieldName> myNames = new HashSet<FieldName>(mapSnd(fields)); |
| 103 | |
| 104 | if(myNames.size() != fields.size()) { |
| 105 | throw new FJException("Duplicate field name(s)"); |
| 106 | } |
| 107 | |
| 108 | if(superNames.removeAll(myNames)) { |
| 109 | throw new FJException("Field name shadowed"); |
| 110 | } |
| 111 | |
| 112 | } |
| 113 | |
| 114 | public List<Tuple<ClassName, FieldName>> getFields() { |
| 115 | return new ArrayList<Tuple<ClassName,FieldName>>(this.fields); |
| 116 | } |
| 117 | |
| 118 | public Maybe<Method> lookupMethod(MethodName mn, ClassTable ct) { |
| 119 | for(Method m : this.methods) { |
| 120 | if(m.getName().equals(mn)) { |
| 121 | return new Just<Method>(m); |
| 122 | } |
| 123 | } |
| 124 | |
| 125 | return ct.mType(mn, this.superClass); |
| 126 | } |
| 127 | |
| 128 | public ClassName getFieldType(FieldName fieldName) throws FJException { |
| 129 | for(Tuple<ClassName, FieldName> field : this.fields) { |
| 130 | if(field.getY().equals(fieldName)) { |
| 131 | return field.getX(); |
| 132 | } |
| 133 | } |
| 134 | |
| 135 | throw new FJException("Looking up a field that doesn't exist!"); |
| 136 | } |
| 137 | |
| 138 | } |