/* * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.apple.internal.jobjc.generator.utils; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import com.apple.internal.jobjc.generator.model.types.NType; import com.apple.internal.jobjc.generator.model.types.NType.NArray; import com.apple.internal.jobjc.generator.model.types.NType.NBitfield; import com.apple.internal.jobjc.generator.model.types.NType.NClass; import com.apple.internal.jobjc.generator.model.types.NType.NField; import com.apple.internal.jobjc.generator.model.types.NType.NObject; import com.apple.internal.jobjc.generator.model.types.NType.NPointer; import com.apple.internal.jobjc.generator.model.types.NType.NPrimitive; import com.apple.internal.jobjc.generator.model.types.NType.NSelector; import com.apple.internal.jobjc.generator.model.types.NType.NStruct; import com.apple.internal.jobjc.generator.model.types.NType.NUnion; import com.apple.internal.jobjc.generator.model.types.NType.NUnknown; import com.apple.internal.jobjc.generator.model.types.NType.NVoid; import com.apple.internal.jobjc.generator.utils.Fp.Dispatcher; import com.apple.internal.jobjc.generator.utils.Fp.Map2; import com.apple.jobjc.JObjCRuntime.Width; /** * Merges two NTypes. All merge does is fill out missing information. It doesn't choose the larger primitive when there's a conflict or anything like that. * * Example: *<pre> * a: {_NSRect={_NSPoint="x"f"y"f}"size"{_NSSize=ff}} * b: {_NSRect="origin"{_NSPoint=ff}{_NSSize="width"f"height"f}} * c: {_NSRect="origin"{_NSPoint="x"f"y"f}"size"{_NSSize="width"f"height"f}} *</pre> */ public class NTypeMerger { public static class MergeFailed extends RuntimeException{ public MergeFailed(String reason, Object a, Object b){ super(reason + " -- (" + a.getClass().getSimpleName() + ") a: " + a + " -- (" + b.getClass().getSimpleName() + ") b: " + b); } } private static NTypeMerger INST = new NTypeMerger(); public static NTypeMerger inst(){ return INST; } /** * Merge a and b. */ public NType merge(NType a, NType b) throws MergeFailed{ if(a!=null && b==null) return a; if(a==null && b!=null) return b; if(a==null && b==null) return null; if(a.equals(b)) return a; try { return Dispatcher.dispatch(getClass(), this, "accept", a, b); } catch (NoSuchMethodException e) { throw new MergeFailed("a and b are of different NType", a, b); } } private static Collection<String> emptyNames = Arrays.asList(null, "", "?"); /** * Merge two identifiers: * - If they're equal, return one. * - If one is null, "", "?", return the other one. * - else throw MergeFailed * * Exception: Due to a bug in BridgeSupport, this will return * a (the first arg) instead of throwing MergeFailed. */ public String mergeName(String a, String b) throws MergeFailed{ if(QA.bothNullOrEquals(a, b)) return a; if(emptyNames.contains(a) && !emptyNames.contains(b)) return b; if(emptyNames.contains(b) && !emptyNames.contains(a)) return a; return a; // HACK BS bug #5954843 // throw new MergeFailed("a and b have different names"); } private Map mergeMap(Map a, Map b) throws MergeFailed{ if(a.equals(b)) return a; Map ret = new HashMap(); Set keys = new HashSet(Fp.append(a.keySet(), b.keySet())); for(Object key : keys){ Object ai = a.get(key); Object bi = b.get(key); if(ai != null && bi == null) ret.put(key, ai); else if(ai == null && bi != null) ret.put(key, bi); else if(ai.equals(bi)) ret.put(key, ai); else throw new MergeFailed("a and b are different", ai, bi); } return ret; } public Map<Width,Integer> mergeSizeOf(Map<Width,Integer> a, Map<Width,Integer> b) throws MergeFailed{ return mergeMap(a, b); } public Map<Width,Integer> mergeOffset(Map<Width,Integer> a, Map<Width,Integer> b) throws MergeFailed{ return mergeMap(a, b); } // private void mustEqual(NType a, NType b){ if(!a.equals(b)) throw new MergeFailed("a must equal b", a, b); } protected NType accept(NBitfield a, NBitfield b) { mustEqual(a, b); return a; } protected NType accept(NPrimitive a, NPrimitive b) { mustEqual(a, b); return a; } protected NType accept(NPointer a, NPointer b) { return new NPointer(NTypeMerger.inst().merge(a.subject, b.subject)); } // Merge structs protected NField mergeNFields(NField a, NField b) { return new NField(mergeName(a.name, b.name), NTypeMerger.inst().merge(a.type, b.type), mergeOffset(a.offset, b.offset)); } protected NStruct mergeStructs(NStruct a, NStruct b){ if(a.fields.size() != b.fields.size()) throw new MergeFailed("a and b have different numbers of fields", a, b); List<NField> fields = Fp.map2(new Map2<NField,NField,NField>(){ public NField apply(NField f32, NField f64) { return mergeNFields(f32, f64); } }, a.fields, b.fields); return new NStruct(mergeName(a.name, b.name), fields, mergeSizeOf(a.sizeof, b.sizeof)); } protected NType accept(NStruct a, NStruct b) { return mergeStructs(a, b); } protected NType accept(NUnion a, NUnion b) { NStruct nst = mergeStructs(a, b); return new NUnion(nst.name, Fp.map(NUnion.zeroOffsets, nst.fields), nst.sizeof); } protected NType accept(NArray a, NArray b) { if(a.length != b.length) throw new MergeFailed("a and b are of different sizes", a, b); return new NArray(a.length, NTypeMerger.inst().merge(a.type, b.type)); } protected NType accept(NVoid a, NVoid b) { return NVoid.inst(); } protected NType accept(NObject a, NObject b) { return NObject.inst(); } protected NType accept(NClass a, NClass b) { return NClass.inst(); } protected NType accept(NSelector a, NSelector b) { return NSelector.inst(); } protected NType accept(NUnknown a, NUnknown b) { return NUnknown.inst(); } }