class SchemaElement implements Nameable, Typeable,
                                               Cloneable {

    public static final int MAX_PRIMITIVE_SYS_ID = 32;
    public static final int NEXT_PRIMITIVE_SYS_ID = 8;

    // ABS of system schema id must be below MAX_PRIMITIVE_SYS_ID
    protected static final int VL_IL_ID = -1;
    protected static final int EL_IL_ID = -2;
    protected static final int PKN_IL_ID = -3;
    protected static final int VLN_IL_ID = -4;
    protected static final int ELN_IL_ID = -5;
    protected static final int ILN_IL_ID = -6;
    protected static final int OLAP_VL_ID = -7;

    public static final Id NONE_ID = IdGenerator.ZERO;

    public static final String UNDEF = "~undefined";

    protected final HugeGraph graph;

    private final Id id;
    private final String name;
    private final Userdata userdata;
    private SchemaStatus status;

    public SchemaElement(final HugeGraph graph, Id id, String name) {
        E.checkArgumentNotNull(id, "SchemaElement id can't be null");
        E.checkArgumentNotNull(name, "SchemaElement name can't be null");
        this.graph = graph;
        this.id = id;
        this.name = name;
        this.userdata = new Userdata();
        this.status = SchemaStatus.CREATED;
    }

    public HugeGraph graph() {
        E.checkState(this.graph != null,
                     "Graph is null of schema '%s'", this.name);
        return this.graph;
    }

    public Id id() {
        return this.id;
    }

    public long longId() {
        return this.id.asLong();
    }

    @Override
    public String name() {
        return this.name;
    }

    public Map<String, Object> userdata() {
        return Collections.unmodifiableMap(this.userdata);
    }

    public void userdata(String key, Object value) {
        E.checkArgumentNotNull(key, "userdata key");
        E.checkArgumentNotNull(value, "userdata value");
        this.userdata.put(key, value);
    }

    public void userdata(Userdata userdata) {
        this.userdata.putAll(userdata);
    }

    public void removeUserdata(String key) {
        E.checkArgumentNotNull(key, "The userdata key can't be null");
        this.userdata.remove(key);
    }

    public void removeUserdata(Userdata userdata) {
        for (String key : userdata.keySet()) {
            this.userdata.remove(key);
        }
    }

    public SchemaStatus status() {
        return this.status;
    }

    public void status(SchemaStatus status) {
        this.status = status;
    }

    public boolean system() {
        return this.longId() < 0L;
    }

    public boolean primitive() {
        long id = this.longId();
        return -MAX_PRIMITIVE_SYS_ID <= id && id < 0L;
    }

    public boolean hidden() {
        return Graph.Hidden.isHidden(this.name());
    }

    public SchemaElement copy() {
        try {
            return (SchemaElement) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new HugeException("Failed to clone schema", e);
        }
    }

    public boolean hasSameContent(SchemaElement other) {
        return Objects.equal(this.name(), other.name()) &&
               Objects.equal(this.userdata(), other.userdata());
    }

    @Override
    public boolean equals(Object obj) {<FILL_FUNCTION_BODY>}

    @Override
    public int hashCode() {
        return this.type().hashCode() ^ this.id.hashCode();
    }

    @Override
    public String toString() {
        return String.format("%s(id=%s)", this.name, this.id);
    }

    public static int schemaId(Id id) {
        long l = id.asLong();
        // Currently we limit the schema id to within 4 bytes
        E.checkArgument(Integer.MIN_VALUE <= l && l <= Integer.MAX_VALUE,
                        "Schema id is out of bound: %s", l);
        return (int) l;
    }

    public static class TaskWithSchema {

        private SchemaElement schemaElement;
        private Id task;

        public TaskWithSchema(SchemaElement schemaElement, Id task) {
            E.checkNotNull(schemaElement, "schema element");
            this.schemaElement = schemaElement;
            this.task = task;
        }

        public void propertyKey(PropertyKey propertyKey) {
            E.checkNotNull(propertyKey, "property key");
            this.schemaElement = propertyKey;
        }

        public void indexLabel(IndexLabel indexLabel) {
            E.checkNotNull(indexLabel, "index label");
            this.schemaElement = indexLabel;
        }

        public PropertyKey propertyKey() {
            E.checkState(this.schemaElement instanceof PropertyKey,
                         "Expect property key, but actual schema type is " +
                         "'%s'", this.schemaElement.getClass());
            return (PropertyKey) this.schemaElement;
        }

        public IndexLabel indexLabel() {
            E.checkState(this.schemaElement instanceof IndexLabel,
                         "Expect index label, but actual schema type is " +
                         "'%s'", this.schemaElement.getClass());
            return (IndexLabel) this.schemaElement;
        }

        public SchemaElement schemaElement() {
            return this.schemaElement;
        }

        public Id task() {
            return this.task;
        }
    }
}

        if (!(obj instanceof SchemaElement)) {
            return false;
        }

        SchemaElement other = (SchemaElement) obj;
        return this.type() == other.type() && this.id.equals(other.id());
    
<no_super_class>