1 /*
   2  * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package com.apple.internal.jobjc.generator.model;
  26 
  27 import java.util.Arrays;
  28 
  29 import org.w3c.dom.Node;
  30 
  31 import com.apple.internal.jobjc.generator.model.types.Type;
  32 import com.apple.internal.jobjc.generator.model.types.JType.JPrimitive;
  33 import com.apple.internal.jobjc.generator.model.types.NType.NPrimitive;
  34 import com.apple.internal.jobjc.generator.utils.Fp;
  35 import com.apple.jobjc.JObjCRuntime;
  36 
  37 public class NativeEnum extends ElementWType<Framework> {
  38     public final String value, value64, le_value, be_value;
  39     public boolean ignore;
  40     public String suggestion;
  41     public NativeEnum(final Node node, final Framework parent) {
  42         super(node, typeForEnum(getAttr(node, "name"),
  43                 getAttr(node, "value"), getAttr(node, "value64"),
  44                 getAttr(node, "le_value"), getAttr(node, "be_value"),
  45                 getAttr(node, "ignore")), parent);
  46         this.value = getAttr(node, "value");
  47         this.value64 = getAttr(node, "value64");
  48         this.le_value = getAttr(node, "le_value");
  49         this.be_value = getAttr(node, "be_value");
  50         String ignoreS = getAttr(node, "ignore");
  51         this.ignore = ignoreS == null ? false : Boolean.parseBoolean(ignoreS);
  52         this.suggestion = getAttr(node, "suggestion");
  53         assert valueToString() != null;
  54     }
  55 
  56     private static Type typeForEnum(String name, String value32, String value64, String le_value, String be_value, String ignore){
  57         if("true".equals(ignore)) return Type.getType(null, NPrimitive.inst('i'), null);
  58 
  59         NumTest[] tests = new NumTest[]{new IntTest(), new LongTest(), new FloatTest(), new DoubleTest()};
  60         for(NumTest t : tests)
  61             if(t.confirm(value32, value64, le_value, be_value))
  62                 return t.getType();
  63 
  64         throw new NumberFormatException(String.format("Failed to parse type for enum: %1$s = 32: %2$s / 64: %3$s / le: %4$s / be: %5$s\n",
  65                 name, value32, value64, le_value, be_value));
  66     }
  67 
  68     public String valueToString(){
  69         if(ignore == true) return "0";
  70         JPrimitive jprim = (JPrimitive) type.getJType();
  71         if(le_value == null && be_value == null){
  72             if(value == null && value64 != null)
  73                 return value64 + jprim.getLiteralSuffix();
  74             else if(value != null && value64 == null)
  75                 return value + jprim.getLiteralSuffix();
  76             else
  77                 return String.format("(%1$s.IS64 ? %2$s%4$s : %3$s%4$s)", JObjCRuntime.class.getName(),
  78                         value64, value, jprim.getLiteralSuffix());
  79         }
  80         else if(value == null && value64 == null){
  81             return String.format("(%1$s.IS_BIG_ENDIAN ? %2$s%4$s : %3$s%4$s)",
  82                     JObjCRuntime.class.getName(), be_value, le_value, jprim.getLiteralSuffix());
  83         }
  84 
  85         throw new RuntimeException("Unable to produce a value for enum " + name);
  86     }
  87 
  88     // Used to find the best type to use for the enum.
  89 
  90     static abstract class NumTest{
  91         public boolean confirm(String... values){
  92             return Fp.all(new Fp.Map1<String,Boolean>(){
  93                 public Boolean apply(String a) {
  94                     try{ return a == null || confirm(a); }
  95                     catch(Exception x){ return false; }
  96                 }},
  97                 Arrays.asList(values));
  98         }
  99 
 100         public abstract boolean confirm(String v);
 101         public abstract Type getType();
 102     }
 103 
 104     static class IntTest extends NumTest{
 105         @Override public boolean confirm(String v) {
 106             Integer.parseInt(v);
 107             return true;
 108         }
 109 
 110         @Override public Type getType() { return Type.getType(null, NPrimitive.inst('i'), null); }
 111     }
 112 
 113     static class LongTest extends NumTest{
 114         @Override public boolean confirm(String v) {
 115             Long.parseLong(v);
 116             return true;
 117         }
 118 
 119         @Override public Type getType() { return Type.getType(null, NPrimitive.inst('l'), null); }
 120     }
 121 
 122     static class FloatTest extends NumTest{
 123         @Override public boolean confirm(String v) {
 124             return Float.parseFloat(v) == Double.parseDouble(v);
 125         }
 126 
 127         @Override public Type getType() { return Type.getType(null, NPrimitive.inst('f'), null); }
 128     }
 129 
 130     static class DoubleTest extends NumTest{
 131         @Override public boolean confirm(String v) {
 132             double d = Double.parseDouble(v);
 133             return !Double.isInfinite(d) && !Double.isNaN(d);
 134         }
 135 
 136         @Override public Type getType() { return Type.getType(null, NPrimitive.inst('d'), null); }
 137     }
 138 }