All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.permazen.OnVersionChangeScanner Maven / Gradle / Ivy

There is a newer version: 5.1.0
Show newest version

/*
 * Copyright (C) 2015 Archie L. Cobbs. All rights reserved.
 */

package io.permazen;

import com.google.common.reflect.TypeToken;

import io.permazen.annotation.OnVersionChange;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

/**
 * Scans for {@link OnVersionChange @OnVersionChange} annotations.
 */
class OnVersionChangeScanner extends AnnotationScanner
  implements Comparator.MethodInfo> {

    private final TypeToken> byStorageIdType;
    private final TypeToken> byNameType;

    @SuppressWarnings("serial")
    OnVersionChangeScanner(JClass jclass) {
        super(jclass, OnVersionChange.class);
        this.byStorageIdType = new TypeToken>() { };
        this.byNameType = new TypeToken>() { };
    }

    @Override
    protected boolean includeMethod(Method method, OnVersionChange annotation) {

        // Sanity check annotation
        if (annotation.oldVersion() < 0)
            throw new IllegalArgumentException("@" + this.annotationType.getSimpleName() + " has illegal negative oldVersion");
        if (annotation.newVersion() < 0)
            throw new IllegalArgumentException("@" + this.annotationType.getSimpleName() + " has illegal negative newVersion");

        // Check method types
        this.checkNotStatic(method);
        this.checkReturnType(method, void.class);
        final int numParams = method.getParameterTypes().length;

        // Handle @OnVersionChange version numbers; allow both to be omitted
        int index = 0;
        if (!(annotation.oldVersion() == 0 && annotation.newVersion() == 0 && numParams == 1)) {
            if (annotation.oldVersion() == 0)
                this.checkParameterType(method, index++, TypeToken.of(int.class));
            if (annotation.newVersion() == 0)
                this.checkParameterType(method, index++, TypeToken.of(int.class));
        }
        final ArrayList> choices = new ArrayList>(2);
        choices.add(this.byStorageIdType);
        choices.add(this.byNameType);
        this.checkParameterType(method, index++, choices);
        if (index != numParams) {
            throw new IllegalArgumentException(this.getErrorPrefix(method)
              + "method has " + (numParams - index) + " too many parameter(s)");
        }

        // Done
        return true;
    }

    @Override
    protected VersionChangeMethodInfo createMethodInfo(Method method, OnVersionChange annotation) {
        return new VersionChangeMethodInfo(method, annotation);
    }

// Comparator

    @Override
    public int compare(MethodInfo info1, MethodInfo info2) {
        final OnVersionChange annotation1 = info1.getAnnotation();
        final OnVersionChange annotation2 = info2.getAnnotation();
        int diff = Boolean.compare(annotation1.oldVersion() == 0, annotation2.oldVersion() == 0);
        if (diff != 0)
            return diff;
        diff = Boolean.compare(annotation1.newVersion() == 0, annotation2.newVersion() == 0);
        if (diff != 0)
            return diff;
        diff = info1.getMethod().getName().compareTo(info2.getMethod().getName());
        if (diff != 0)
            return diff;
        return 0;
    }

// VersionChangeMethodInfo

    class VersionChangeMethodInfo extends MethodInfo {

        private final boolean byName;

        @SuppressWarnings("unchecked")
        VersionChangeMethodInfo(Method method, OnVersionChange annotation) {
            super(method, annotation);
            final List> actuals = OnVersionChangeScanner.this.getParameterTypeTokens(method);
            final TypeToken oldValuesType = actuals.get(actuals.size() - 1);
            if (oldValuesType.equals(OnVersionChangeScanner.this.byStorageIdType))
                this.byName = false;
            else if (oldValuesType.equals(OnVersionChangeScanner.this.byNameType))
                this.byName = true;
            else
                throw new RuntimeException("internal error");
        }

        // Invoke method
        void invoke(JObject jobj, int oldVersion, int newVersion,
          Map oldValuesByStorageId, Map oldValuesByName) {

            // Get method info
            final OnVersionChange annotation = this.getAnnotation();
            final Method method = this.getMethod();

            // Check old & new version numbers
            if ((annotation.oldVersion() != 0 && annotation.oldVersion() != oldVersion)
              || (annotation.newVersion() != 0 && annotation.newVersion() != newVersion))
                return;

            // Determine which map to provide
            Map oldValues = this.byName ? oldValuesByName : oldValuesByStorageId;

            // Invoke method
            switch ((annotation.oldVersion() != 0 ? 2 : 0) + (annotation.newVersion() != 0 ? 1 : 0)) {
            case 0:
                Util.invoke(method, jobj, oldVersion, newVersion, oldValues);
                break;
            case 1:
                Util.invoke(method, jobj, oldVersion, oldValues);
                break;
            case 2:
                Util.invoke(method, jobj, newVersion, oldValues);
                break;
            case 3:
            default:
                Util.invoke(method, jobj, oldValues);
                break;
            }
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == this)
                return true;
            if (!super.equals(obj))
                return false;
            final OnVersionChangeScanner.VersionChangeMethodInfo that = (OnVersionChangeScanner.VersionChangeMethodInfo)obj;
            return this.byName == that.byName;
        }

        @Override
        public int hashCode() {
            return super.hashCode()
              ^ (this.byName ? 1 : 0);
        }
    }
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy