File indexing completed on 2024-12-15 04:19:54

0001 package mso.generator.utils;
0002 
0003 import java.util.ArrayList;
0004 import java.util.List;
0005 
0006 import org.eclipse.jdt.annotation.Nullable;
0007 
0008 public class Choice extends Type {
0009 
0010     public final @Nullable Type commonType;
0011     public final List<Option> options = new ArrayList<Option>();
0012 
0013     public String[] getChoiceNames() {
0014         String n[] = new String[options.size()];
0015         for (int i = 0; i < options.size(); ++i) {
0016             n[i] = options.get(i).type.name;
0017         }
0018         return n;
0019     }
0020 
0021     Choice(TypeRegistry registry, String name, List<Struct> choices,
0022             boolean optional) {
0023         super(registry, name, getSize(choices));
0024         Type common = null;
0025         for (Struct s : choices) {
0026             Option o = Option.parseOption(s, common);
0027             if (common != null && o.limitsType != common
0028                     && !compareTypes(common, o.limitsType)) {
0029                 throw new Error("Conflicting common type: " + common.name
0030                         + " vs " + o.limitsType.name);
0031             }
0032             common = o.limitsType;
0033             options.add(o);
0034         }
0035         // check if the common type is the same
0036         for (Option o : options) {
0037             if (!compareTypes(common, o.limitsType)) {
0038                 String types = "";
0039                 for (Option op : options) {
0040                     types = types + " " + op.type.name;
0041                 }
0042                 System.err.println("The choice " + name
0043                         + " has no common options for types" + types);
0044                 common = null;
0045                 break;
0046             }
0047         }
0048         if (common == null) {
0049             commonType = null;
0050             return;
0051         }
0052         // Check if the limitations are distinctive
0053         // if limitation on an earlier option are a subset of a later
0054         // limitation,
0055         // then then the limitations cannot be used for making the choice
0056         // so must make sure that earlier sets of limitations are not
0057         // subsets of later limitations.
0058         for (int i = 0; i < options.size(); ++i) {
0059             int last = (optional) ? options.size() : options.size() - 1;
0060             for (int j = i + 1; j < last; ++j) {
0061                 // the double negation is important: we cannot be sure that it
0062                 // is a subset
0063                 // we must be sure that it is not a subset
0064                 Option a = options.get(i);
0065                 Option b = options.get(j);
0066                 if (!noOverlap(a.lim, b.lim)) {
0067                     System.err.println("Options " + a.type.name + " and "
0068                             + b.type.name + " have overlap.");
0069                     common = null;
0070                 }
0071             }
0072         }
0073 
0074         // if the options are distinctive we can use them
0075         commonType = common;
0076 
0077         if (!optional && common != null) {
0078             // remove options that are present in all options
0079             removeNonDistinctiveLimitations(options);
0080         }
0081     }
0082 
0083     // if all choices have equal size, return that sizes, else return -1
0084     private static int getSize(List<Struct> choices) {
0085         int size = -1;
0086         for (Struct s : choices) {
0087             if (size == -1) {
0088                 size = s.size;
0089             }
0090             if (s.size != size) {
0091                 return -1;
0092             }
0093         }
0094         return size;
0095     }
0096 
0097     /**
0098      * Return true if not all of the limitations in a are sure to be present in
0099      * b. Err on the side of safety: if unsure return false;
0100      */
0101     private static boolean noOverlap(Lim a, Lim b) {
0102         if (a.limitations == null && a.lims == null)
0103             return false;
0104         if (a.limitations != null) {
0105             if (!noOverlap(a.limitations, b)) {
0106                 return false;
0107             }
0108         }
0109         if (a.lims != null) {
0110             for (Lim sa : a.lims) {
0111                 if (!noOverlap(sa, b)) {
0112                     return false;
0113                 }
0114             }
0115         }
0116         return true;
0117     }
0118 
0119     private static boolean noOverlap(Limitation a[], Lim b) {
0120         if (b.limitations == null && b.lims == null)
0121             return false;
0122         if (b.limitations != null) {
0123             if (!noOverlap(a, b.limitations)) {
0124                 return false;
0125             }
0126         }
0127         if (b.lims != null) {
0128             for (Lim sb : b.lims) {
0129                 if (!noOverlap(a, sb)) {
0130                     return false;
0131                 }
0132             }
0133         }
0134         return true;
0135     }
0136 
0137     /**
0138      * Return true if sure that a and b define value spaces with no overlap
0139      */
0140     private static boolean noOverlap(Limitation a[], Limitation b[]) {
0141         for (int i = 0; i < a.length; ++i) {
0142             for (int j = 0; j < b.length; ++j) {
0143                 if (a[i].name.equals(b[j].name)
0144                         && noOverlap(a[i].value, b[j].value)) {
0145                     return true;
0146                 }
0147             }
0148         }
0149         return false;
0150     }
0151 
0152     private static boolean noOverlap(@Nullable String a, @Nullable String b) {
0153         if (a == null || b == null)
0154             return false;
0155         String[] avalues = a.split("\\|");
0156         String[] bvalues = b.split("\\|");
0157         for (int i = 0; i < avalues.length; ++i) {
0158             for (int j = 0; j < bvalues.length; ++j) {
0159                 if (avalues[i].equals(bvalues[j])) {
0160                     return false;
0161                 }
0162             }
0163         }
0164         return true;
0165     }
0166 
0167     private static boolean compareMembers(Member a, Member b) {
0168         if (a.isArray != b.isArray)
0169             return false;
0170         if (a.type() != b.type())
0171             return false;
0172         if (a.isOptional != b.isOptional)
0173             return false;
0174         return true;
0175     }
0176 
0177     private static boolean structsWithSameMembers(@Nullable Type a, Type b) {
0178         if (a instanceof Struct && b instanceof Struct) {
0179             Struct as = (Struct) a;
0180             Struct bs = (Struct) b;
0181             if (as.members.size() != bs.members.size()) {
0182                 return false;
0183             }
0184             for (int i = 0; i < as.members.size(); ++i) {
0185                 if (!compareMembers(as.members.get(i), bs.members.get(i))) {
0186                     return false;
0187                 }
0188             }
0189             return true;
0190         }
0191         return false;
0192     }
0193 
0194     private static boolean compareTypes(@Nullable Type a, Type b) {
0195         return a == b || structsWithSameMembers(a, b);
0196     }
0197 
0198     private static void removeNonDistinctiveLimitations(List<Option> options) {
0199         final Lim a = options.get(0).lim;
0200         final Limitation limitations[] = a.limitations;
0201         if (limitations != null) {
0202             for (int i = 0; i < limitations.length; ++i) {
0203                 Limitation l = limitations[i];
0204                 boolean common = true;
0205                 for (int j = 1; j < options.size(); ++j) {
0206                     common = common
0207                             && options.get(j).lim.containsCommonLimitation(l);
0208                 }
0209                 if (common) {
0210                     for (int j = 0; j < options.size(); ++j) {
0211                         options.get(j).lim.removeLimitation(l);
0212                     }
0213                     i = 0;
0214                 }
0215             }
0216         }
0217     }
0218 }