class ExprModel {

    public void seal() {
        L.d("sealing model");
        List<Expr> notifiableExpressions = new ArrayList<Expr>();
        //ensure class analyzer. We need to know observables at this point
        final ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance();
        updateExpressions(modelAnalyzer);

        int counter = 0;
        final Iterable<Expr> observables = filterObservables(modelAnalyzer);
        List<String> flagMapping = new ArrayList<String>();
        mObservables = new ArrayList<Expr>();
        for (Expr expr : observables) {
            // observables gets initial ids
            flagMapping.add(expr.getUniqueKey());
            expr.setId(counter++);
            mObservables.add(expr);
            notifiableExpressions.add(expr);
            L.d("observable %s", expr.getUniqueKey());
        }

        // non-observable identifiers gets next ids
        final Iterable<Expr> nonObservableIds = filterNonObservableIds(modelAnalyzer);
        for (Expr expr : nonObservableIds) {
            flagMapping.add(expr.getUniqueKey());
            expr.setId(counter++);
            notifiableExpressions.add(expr);
            L.d("non-observable %s", expr.getUniqueKey());
        }

        // descendants of observables gets following ids
        for (Expr expr : observables) {
            for (Expr parent : expr.getParents()) {
                if (parent.hasId()) {
                    continue;// already has some id, means observable
                }
                // only fields earn an id
                if (parent instanceof FieldAccessExpr) {
                    FieldAccessExpr fae = (FieldAccessExpr) parent;
                    L.d("checking field access expr %s. getter: %s", fae,fae.getGetter());
                    // FAE#getter might be null if it is used only in a callback.
                    if (fae.getGetter() != null && fae.isDynamic()
                            && fae.getGetter().canBeInvalidated()) {
                        flagMapping.add(parent.getUniqueKey());
                        parent.setId(counter++);
                        notifiableExpressions.add(parent);
                        L.d("notifiable field %s : %s for %s : %s", parent.getUniqueKey(),
                                Integer.toHexString(System.identityHashCode(parent)),
                                expr.getUniqueKey(),
                                Integer.toHexString(System.identityHashCode(expr)));
                    }
                }
            }
        }

        // now all 2-way bound view fields
        for (Expr expr : mExprMap.values()) {
            if (expr instanceof FieldAccessExpr) {
                FieldAccessExpr fieldAccessExpr = (FieldAccessExpr) expr;
                if (fieldAccessExpr.getTarget() instanceof ViewFieldExpr) {
                    flagMapping.add(fieldAccessExpr.getUniqueKey());
                    fieldAccessExpr.setId(counter++);
                }
            }
        }

        // non-dynamic binding expressions receive some ids so that they can be invalidated
        L.d("list of binding expressions");
        for (int i = 0; i < mBindingExpressions.size(); i++) {
            L.d("[%d] %s", i, mBindingExpressions.get(i));
        }
        // we don't assign ids to constant binding expressions because now invalidateAll has its own
        // flag.

        for (Expr expr : notifiableExpressions) {
            expr.enableDirectInvalidation();
        }

        // make sure all dependencies are resolved to avoid future race conditions
        for (Expr expr : mExprMap.values()) {
            expr.getDependencies();
        }
        mInvalidateAnyFlagIndex = counter ++;
        flagMapping.add("INVALIDATE ANY");
        mInvalidateableFieldLimit = counter;
        BitSet invalidateableFlags = new BitSet();
        for (int i = 0; i < mInvalidateableFieldLimit; i++) {
            invalidateableFlags.set(i, true);
        }

        // make sure all dependencies are resolved to avoid future race conditions
        for (Expr expr : mExprMap.values()) {
            if (expr.isConditional()) {
                L.d("requirement id for %s is %d", expr, counter);
                expr.setRequirementId(counter);
                flagMapping.add(expr.getUniqueKey() + FALSE_KEY_SUFFIX);
                flagMapping.add(expr.getUniqueKey() + TRUE_KEY_SUFFIX);
                counter += 2;
            }
        }
        BitSet conditionalFlags = new BitSet();
        for (int i = mInvalidateableFieldLimit; i < counter; i++) {
            conditionalFlags.set(i, true);
        }
        mRequirementIdCount = (counter - mInvalidateableFieldLimit) / 2;

        // everybody gets an id
        for (Map.Entry<String, Expr> entry : mExprMap.entrySet()) {
            final Expr value = entry.getValue();
            if (!value.hasId()) {
                value.setId(counter++);
            }
        }

        mFlagMapping = new String[flagMapping.size()];
        flagMapping.toArray(mFlagMapping);

        mFlagBucketCount = 1 + (getTotalFlagCount() / FlagSet.sBucketSize);
        mInvalidateAnyFlags = new BitSet();
        mInvalidateAnyFlags.set(mInvalidateAnyFlagIndex, true);

        for (Expr expr : mExprMap.values()) {
            expr.getShouldReadFlagsWithConditionals();
        }

        for (Expr expr : mExprMap.values()) {
            // ensure all types are calculated
            expr.getResolvedType();
        }

        mSealed = true;
    }

}