Implement #1029 (add support for `@JsonAlias`)
diff --git a/release-notes/VERSION b/release-notes/VERSION
index 15cd103..aba37ee 100644
--- a/release-notes/VERSION
+++ b/release-notes/VERSION
@@ -19,6 +19,7 @@
 #865: `JsonFormat.Shape.OBJECT` ignored when class implements `Map.Entry`
 #888: Allow specifying custom exclusion comparator via `@JsonInclude`,
   using `JsonInclude.Include.CUSTOM`
+#1029: Add a way to define property name aliases
 #1035: `@JsonAnySetter` assumes key of `String`, does not consider declared type.
  (reported by Michael F)
 #1284: Make `StdKeySerializers` use new `JsonGenerator.writeFieldId()` for `int`/`long` keys
diff --git a/src/main/java/com/fasterxml/jackson/databind/BeanProperty.java b/src/main/java/com/fasterxml/jackson/databind/BeanProperty.java
index ac35793..b68bf24 100644
--- a/src/main/java/com/fasterxml/jackson/databind/BeanProperty.java
+++ b/src/main/java/com/fasterxml/jackson/databind/BeanProperty.java
@@ -1,6 +1,8 @@
 package com.fasterxml.jackson.databind;
 
 import java.lang.annotation.Annotation;
+import java.util.Collections;
+import java.util.List;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fasterxml.jackson.annotation.JsonFormat.Value;
@@ -164,6 +166,16 @@
      */
     public JsonInclude.Value findPropertyInclusion(MapperConfig<?> config, Class<?> baseType);
 
+    /**
+     * Method for accessing set of possible alternate names that are accepted
+     * during deserialization.
+     *
+     * @return List (possibly empty) of alternate names; never null
+     *
+     * @since 2.9
+     */
+    public List<PropertyName> findAliases(MapperConfig<?> config);
+
     /*
     /**********************************************************
     /* Schema/introspection support
@@ -300,6 +312,13 @@
             return v0.withOverrides(v);
         }
 
+        @Override
+        public List<PropertyName> findAliases(MapperConfig<?> config) {
+            // 26-Feb-2017, tatu: Do we really need to allow actual definition?
+            //    For now, let's not.
+            return Collections.emptyList();
+        }
+
         @Override public String getName() { return _name.getSimpleName(); }
         @Override public PropertyName getFullName() { return _name; }
         @Override public JavaType getType() { return _type; }
@@ -401,6 +420,11 @@
         }
 
         @Override
+        public List<PropertyName> findAliases(MapperConfig<?> config) {
+            return Collections.emptyList();
+        }
+
+        @Override
         public void depositSchemaProperty(JsonObjectFormatVisitor objectVisitor,
                 SerializerProvider provider) throws JsonMappingException {
         }
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java
index 6adfa31..83dba24 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java
@@ -586,7 +586,8 @@
 
         // And now that we know CreatorProperty instances are also resolved can finally create the creator:
         if (creatorProps != null) {
-            _propertyBasedCreator = PropertyBasedCreator.construct(ctxt, _valueInstantiator, creatorProps);
+            _propertyBasedCreator = PropertyBasedCreator.construct(ctxt, _valueInstantiator,
+                    creatorProps, _beanProperties);
         }
 
         if (extTypes != null) {
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBuilder.java b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBuilder.java
index e374fb2..43fdd24 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBuilder.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBuilder.java
@@ -340,7 +340,8 @@
         _fixAccess(props);
 
         BeanPropertyMap propertyMap = BeanPropertyMap.construct(props,
-                _config.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES));
+                _config.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES),
+                _collectAliases(props));
         propertyMap.assignIndexes();
 
         // view processing must be enabled if:
@@ -416,7 +417,8 @@
         Collection<SettableBeanProperty> props = _properties.values();
         _fixAccess(props);
         BeanPropertyMap propertyMap = BeanPropertyMap.construct(props,
-                _config.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES));
+                _config.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES),
+                _collectAliases(props));
         propertyMap.assignIndexes();
 
         boolean anyViews = !_config.isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION);
@@ -449,7 +451,7 @@
     /**********************************************************
      */
 
-    private void _fixAccess(Collection<SettableBeanProperty> mainProps)
+    protected void _fixAccess(Collection<SettableBeanProperty> mainProps)
     {
         /* 07-Sep-2016, tatu: Ideally we should be able to avoid forcing
          *   access to properties that are likely ignored, but due to
@@ -488,4 +490,26 @@
             _buildMethod.fixAccess(_config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
         }
     }
+
+    protected Map<String,List<PropertyName>> _collectAliases(Collection<SettableBeanProperty> props)
+    {
+        Map<String,List<PropertyName>> mapping = null;
+        AnnotationIntrospector intr = _config.getAnnotationIntrospector();
+        if (intr != null) {
+            for (SettableBeanProperty prop : props) {
+                List<PropertyName> aliases = intr.findPropertyAliases(prop.getMember());
+                if ((aliases == null) || aliases.isEmpty()) {
+                    continue;
+                }
+                if (mapping == null) {
+                    mapping = new HashMap<>();
+                }
+                mapping.put(prop.getName(), aliases);
+            }
+        }
+        if (mapping == null) {
+            return Collections.emptyMap();
+        }
+        return mapping;
+    }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanPropertyMap.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanPropertyMap.java
index ea0ef72..9308fb7 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanPropertyMap.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanPropertyMap.java
@@ -10,6 +10,7 @@
 import com.fasterxml.jackson.databind.DeserializationFeature;
 import com.fasterxml.jackson.databind.JsonDeserializer;
 import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.PropertyName;
 import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
 import com.fasterxml.jackson.databind.util.ClassUtil;
 import com.fasterxml.jackson.databind.util.NameTransformer;
@@ -56,22 +57,54 @@
      */
     private SettableBeanProperty[] _propsInOrder;
 
-    public BeanPropertyMap(boolean caseInsensitive, Collection<SettableBeanProperty> props)
+    /**
+     * Configuration of alias mappings, indexed by unmodified property name
+     * to unmodified aliases, if any; entries only included for properties
+     * that do have aliases.
+     * This is is used for constructing actual reverse lookup mapping, if
+     * needed, taking into account possible case-insensitivity, as well
+     * as possibility of name prefixes.
+     *
+     * @since 2.9
+     */
+    private final Map<String,List<PropertyName>> _aliasDefs;
+
+    /**
+     * Mapping from secondary names (aliases) to primary names.
+     *
+     * @since 2.9
+     */
+    private final Map<String,String> _aliasMapping;
+    
+    /**
+     * @since 2.9
+     */
+    public BeanPropertyMap(boolean caseInsensitive, Collection<SettableBeanProperty> props,
+            Map<String,List<PropertyName>> aliasDefs)
     {
         _caseInsensitive = caseInsensitive;
         _propsInOrder = props.toArray(new SettableBeanProperty[props.size()]);
+        _aliasDefs = aliasDefs;
+        _aliasMapping = _buildAliasMapping(aliasDefs);
         init(props);
     }
 
+    @Deprecated // since 2.8
+    public BeanPropertyMap(boolean caseInsensitive, Collection<SettableBeanProperty> props)
+    {
+        this(caseInsensitive, props, Collections.<String,List<PropertyName>>emptyMap());
+    }
+
     /**
      * @since 2.8
      */
     protected BeanPropertyMap(BeanPropertyMap base, boolean caseInsensitive)
     {
         _caseInsensitive = caseInsensitive;
+        _aliasDefs = base._aliasDefs;
+        _aliasMapping = base._aliasMapping;
 
-        // 16-May-2016, tatu: Alas, not enough to just change flag, need to re-init
-        //    as well.
+        // 16-May-2016, tatu: Alas, not enough to just change flag, need to re-init as well.
         _propsInOrder = Arrays.copyOf(base._propsInOrder, base._propsInOrder.length);
         init(Arrays.asList(_propsInOrder));
     }
@@ -108,7 +141,7 @@
             if (prop == null) {
                 continue;
             }
-            
+
             String key = getPropertyName(prop);
             int slot = _hashCode(key);
             int ix = (slot<<1);
@@ -129,6 +162,8 @@
 //System.err.println(" add '"+key+" at #"+(ix>>1)+"/"+size+" (hashed at "+slot+")");             
             hashed[ix] = key;
             hashed[ix+1] = prop;
+
+            // and aliases
         }
 /*
 for (int i = 0; i < hashed.length; i += 2) {
@@ -138,7 +173,7 @@
         _hashArea = hashed;
         _spillCount = spillCount;
     }
-    
+
     private final static int findSize(int size)
     {
         if (size <= 5) {
@@ -158,10 +193,17 @@
     /**
      * @since 2.6
      */
-    public static BeanPropertyMap construct(Collection<SettableBeanProperty> props, boolean caseInsensitive) {
-        return new BeanPropertyMap(caseInsensitive, props);
+    public static BeanPropertyMap construct(Collection<SettableBeanProperty> props,
+            boolean caseInsensitive, Map<String,List<PropertyName>> aliasMapping) {
+        return new BeanPropertyMap(caseInsensitive, props, aliasMapping);
     }
-    
+
+    @Deprecated // since 2.9
+    public static BeanPropertyMap construct(Collection<SettableBeanProperty> props, boolean caseInsensitive) {
+        return construct(props, caseInsensitive,
+                Collections.<String,List<PropertyName>>emptyMap());
+    }
+
     /**
      * Fluent copy method that creates a new instance that is a copy
      * of this instance except for one additional property that is
@@ -259,9 +301,16 @@
             newProps.add(_rename(prop, transformer));
         }
         // should we try to re-index? Ordering probably changed but caller probably doesn't want changes...
-        return new BeanPropertyMap(_caseInsensitive, newProps);
+        // 26-Feb-2017, tatu: Probably SHOULD handle renaming wrt Aliases?
+        return new BeanPropertyMap(_caseInsensitive, newProps, _aliasDefs);
     }
 
+    /*
+    /**********************************************************
+    /* Public API, mutators
+    /**********************************************************
+     */
+    
     /**
      * Mutant factory method that will use this instance as the base, and
      * construct an instance that is otherwise same except for excluding
@@ -289,9 +338,9 @@
             }
         }
         // should we try to re-index? Apparently no need
-        return new BeanPropertyMap(_caseInsensitive, newProps);
+        return new BeanPropertyMap(_caseInsensitive, newProps, _aliasDefs);
     }
-    
+
     /**
      * Specialized method that can be used to replace an existing entry
      * (note: entry MUST exist; otherwise exception is thrown) with
@@ -310,113 +359,6 @@
         _propsInOrder[_findFromOrdered(prop)] = newProp;
     }
 
-    private List<SettableBeanProperty> properties() {
-        ArrayList<SettableBeanProperty> p = new ArrayList<SettableBeanProperty>(_size);
-        for (int i = 1, end = _hashArea.length; i < end; i += 2) {
-            SettableBeanProperty prop = (SettableBeanProperty) _hashArea[i];
-            if (prop != null) {
-                p.add(prop);
-            }
-        }
-        return p;
-    }
-
-    /**
-     * Accessor for traversing over all contained properties.
-     */
-    @Override
-    public Iterator<SettableBeanProperty> iterator() {
-        return properties().iterator();
-    }
-
-    /**
-     * Method that will re-create initial insertion-ordering of
-     * properties contained in this map. Note that if properties
-     * have been removed, array may contain nulls; otherwise
-     * it should be consecutive.
-     * 
-     * @since 2.1
-     */
-    public SettableBeanProperty[] getPropertiesInInsertionOrder() {
-        return _propsInOrder;
-    }
-
-    // Confining this case insensitivity to this function (and the find method) in case we want to
-    // apply a particular locale to the lower case function.  For now, using the default.
-    protected final String getPropertyName(SettableBeanProperty prop) {
-        return _caseInsensitive ? prop.getName().toLowerCase() : prop.getName();
-    }
-
-    /**
-     * @since 2.3
-     */
-    public SettableBeanProperty find(int index)
-    {
-        // note: will scan the whole area, including primary, secondary and
-        // possible spill-area
-        for (int i = 1, end = _hashArea.length; i < end; i += 2) {
-            SettableBeanProperty prop = (SettableBeanProperty) _hashArea[i];
-            if ((prop != null) && (index == prop.getPropertyIndex())) {
-                return prop;
-            }
-        }
-        return null;
-    }
-
-    public SettableBeanProperty find(String key)
-    {
-        if (key == null) {
-            throw new IllegalArgumentException("Can not pass null property name");
-        }
-        if (_caseInsensitive) {
-            key = key.toLowerCase();
-        }
-            
-        // inlined `_hashCode(key)`
-        int slot = key.hashCode() & _hashMask;
-//        int h = key.hashCode();
-//        int slot = (h + (h >> 13)) & _hashMask;
-
-        int ix = (slot<<1);
-        Object match = _hashArea[ix];
-        if ((match == key) || key.equals(match)) {
-            return (SettableBeanProperty) _hashArea[ix+1];
-        }
-        return _find2(key, slot, match);
-    }
-
-    private final SettableBeanProperty _find2(String key, int slot, Object match)
-    {
-        if (match == null) {
-            return null;
-        }
-        // no? secondary?
-        int hashSize = _hashMask+1;
-        int ix = hashSize + (slot>>1) << 1;
-        match = _hashArea[ix];
-        if (key.equals(match)) {
-            return (SettableBeanProperty) _hashArea[ix+1];
-        }
-        if (match != null) { // _findFromSpill(...)
-            int i = (hashSize + (hashSize>>1)) << 1;
-            for (int end = i + _spillCount; i < end; i += 2) {
-                match = _hashArea[i];
-                if ((match == key) || key.equals(match)) {
-                    return (SettableBeanProperty) _hashArea[i+1];
-                }
-            }
-        }
-        return null;
-    }
-    
-    /*
-    /**********************************************************
-    /* Public API
-    /**********************************************************
-     */
-
-    public int size() { return _size; }
-
     /**
      * Specialized method for removing specified existing entry.
      * NOTE: entry MUST exist, otherwise an exception is thrown.
@@ -450,6 +392,181 @@
         init(props);
     }
 
+    /*
+    /**********************************************************
+    /* Public API, simple accessors
+    /**********************************************************
+     */
+
+    public int size() { return _size; }
+
+    /**
+     * @since 2.9
+     */
+    public boolean isCaseInsensitive() {
+        return _caseInsensitive;
+    }
+
+    /**
+     * @since 2.9
+     */
+    public boolean hasAliases() {
+        return !_aliasDefs.isEmpty();
+    }
+
+    /**
+     * Accessor for traversing over all contained properties.
+     */
+    @Override
+    public Iterator<SettableBeanProperty> iterator() {
+        return _properties().iterator();
+    }
+
+    private List<SettableBeanProperty> _properties() {
+        ArrayList<SettableBeanProperty> p = new ArrayList<SettableBeanProperty>(_size);
+        for (int i = 1, end = _hashArea.length; i < end; i += 2) {
+            SettableBeanProperty prop = (SettableBeanProperty) _hashArea[i];
+            if (prop != null) {
+                p.add(prop);
+            }
+        }
+        return p;
+    }
+    
+    /**
+     * Method that will re-create initial insertion-ordering of
+     * properties contained in this map. Note that if properties
+     * have been removed, array may contain nulls; otherwise
+     * it should be consecutive.
+     * 
+     * @since 2.1
+     */
+    public SettableBeanProperty[] getPropertiesInInsertionOrder() {
+        return _propsInOrder;
+    }
+
+    // Confining this case insensitivity to this function (and the find method) in case we want to
+    // apply a particular locale to the lower case function.  For now, using the default.
+    protected final String getPropertyName(SettableBeanProperty prop) {
+        return _caseInsensitive ? prop.getName().toLowerCase() : prop.getName();
+    }
+
+    /*
+    /**********************************************************
+    /* Public API, property lookup
+    /**********************************************************
+     */
+    
+    /**
+     * @since 2.3
+     */
+    public SettableBeanProperty find(int index)
+    {
+        // note: will scan the whole area, including primary, secondary and
+        // possible spill-area
+        for (int i = 1, end = _hashArea.length; i < end; i += 2) {
+            SettableBeanProperty prop = (SettableBeanProperty) _hashArea[i];
+            if ((prop != null) && (index == prop.getPropertyIndex())) {
+                return prop;
+            }
+        }
+        return null;
+    }
+
+    public SettableBeanProperty find(String key)
+    {
+        if (key == null) {
+            throw new IllegalArgumentException("Can not pass null property name");
+        }
+        if (_caseInsensitive) {
+            key = key.toLowerCase();
+        }
+
+        // inlined `_hashCode(key)`
+        int slot = key.hashCode() & _hashMask;
+//        int h = key.hashCode();
+//        int slot = (h + (h >> 13)) & _hashMask;
+
+        int ix = (slot<<1);
+        Object match = _hashArea[ix];
+        if ((match == key) || key.equals(match)) {
+            return (SettableBeanProperty) _hashArea[ix+1];
+        }
+        return _find2(key, slot, match);
+    }
+
+    private final SettableBeanProperty _find2(String key, int slot, Object match)
+    {
+        if (match == null) {
+            // 26-Feb-2017, tatu: Need to consider aliases
+            return _findWithAlias(_aliasMapping.get(key));
+        }
+        // no? secondary?
+        int hashSize = _hashMask+1;
+        int ix = hashSize + (slot>>1) << 1;
+        match = _hashArea[ix];
+        if (key.equals(match)) {
+            return (SettableBeanProperty) _hashArea[ix+1];
+        }
+        if (match != null) { // _findFromSpill(...)
+            int i = (hashSize + (hashSize>>1)) << 1;
+            for (int end = i + _spillCount; i < end; i += 2) {
+                match = _hashArea[i];
+                if ((match == key) || key.equals(match)) {
+                    return (SettableBeanProperty) _hashArea[i+1];
+                }
+            }
+        }
+        // 26-Feb-2017, tatu: Need to consider aliases
+        return _findWithAlias(_aliasMapping.get(key));
+    }
+
+    private SettableBeanProperty _findWithAlias(String keyFromAlias)
+    {
+        if (keyFromAlias == null) {
+            return null;
+        }
+        // NOTE: need to inline much of handling do avoid cyclic calls via alias
+        // first, inlined main `find(String)`
+        int slot = _hashCode(keyFromAlias);
+        int ix = (slot<<1);
+        Object match = _hashArea[ix];
+        if (keyFromAlias.equals(match)) {
+            return (SettableBeanProperty) _hashArea[ix+1];
+        }
+        if (match == null) {
+            return null;
+        }
+        return _find2ViaAlias(keyFromAlias, slot, match);
+    }
+
+    private SettableBeanProperty _find2ViaAlias(String key, int slot, Object match)
+    {
+        // no? secondary?
+        int hashSize = _hashMask+1;
+        int ix = hashSize + (slot>>1) << 1;
+        match = _hashArea[ix];
+        if (key.equals(match)) {
+            return (SettableBeanProperty) _hashArea[ix+1];
+        }
+        if (match != null) { // _findFromSpill(...)
+            int i = (hashSize + (hashSize>>1)) << 1;
+            for (int end = i + _spillCount; i < end; i += 2) {
+                match = _hashArea[i];
+                if ((match == key) || key.equals(match)) {
+                    return (SettableBeanProperty) _hashArea[i+1];
+                }
+            }
+        }
+        return null;
+    }
+
+    /*
+    /**********************************************************
+    /* Public API, deserialization support
+    /**********************************************************
+     */
+
     /**
      * Convenience method that tries to find property with given name, and
      * if it is found, call {@link SettableBeanProperty#deserializeAndSet}
@@ -474,6 +591,12 @@
         return true;
     }
 
+    /*
+    /**********************************************************
+    /* Std method overrides
+    /**********************************************************
+     */
+    
     @Override
     public String toString()
     {
@@ -493,6 +616,11 @@
             sb.append(')');
         }
         sb.append(']');
+        if (!_aliasDefs.isEmpty()) {
+            sb.append("(aliases: ");
+            sb.append(_aliasDefs);
+            sb.append(")");
+        }
         return sb.toString();
     }
     
@@ -598,4 +726,27 @@
         */
         return key.hashCode() & _hashMask;
     }
+
+    // @since 2.9
+    private Map<String,String> _buildAliasMapping(Map<String,List<PropertyName>> defs)
+    {
+        if ((defs == null) || defs.isEmpty()) {
+            return Collections.emptyMap();
+        }
+        Map<String,String> aliases = new HashMap<>();
+        for (Map.Entry<String,List<PropertyName>> entry : defs.entrySet()) {
+            String key = entry.getKey();
+            if (_caseInsensitive) {
+                key = key.toLowerCase();
+            }
+            for (PropertyName pn : entry.getValue()) {
+                String mapped = pn.getSimpleName();
+                if (_caseInsensitive) {
+                    mapped = mapped.toLowerCase();
+                }
+                aliases.put(mapped, key);
+            }
+        }
+        return aliases;
+    }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyBasedCreator.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyBasedCreator.java
index f0c4093..4e7bc44 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyBasedCreator.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyBasedCreator.java
@@ -1,14 +1,11 @@
 package com.fasterxml.jackson.databind.deser.impl;
 
 import java.io.IOException;
-import java.util.Collection;
-import java.util.HashMap;
+import java.util.*;
 
 import com.fasterxml.jackson.core.JsonParser;
 
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.JsonMappingException;
-import com.fasterxml.jackson.databind.MapperFeature;
+import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
 import com.fasterxml.jackson.databind.deser.ValueInstantiator;
 
@@ -53,9 +50,11 @@
     /**********************************************************
      */
 
-    protected PropertyBasedCreator(ValueInstantiator valueInstantiator,
+    protected PropertyBasedCreator(DeserializationContext ctxt,
+            ValueInstantiator valueInstantiator,
             SettableBeanProperty[] creatorProps,
-            boolean caseInsensitive)
+            boolean caseInsensitive,
+            boolean addAliases)
     {
         _valueInstantiator = valueInstantiator;
         if (caseInsensitive) {
@@ -66,6 +65,20 @@
         final int len = creatorProps.length;
         _propertyCount = len;
         _allProperties = new SettableBeanProperty[len];
+
+        // 26-Feb-2017, tatu: Let's start by aliases, so that there is no
+        //    possibility of accidental override of primary names
+        if (addAliases) {
+            final DeserializationConfig config = ctxt.getConfig();
+            for (SettableBeanProperty prop : creatorProps) {
+                List<PropertyName> aliases = prop.findAliases(config);
+                if (!aliases.isEmpty()) {
+                    for (PropertyName pn : aliases) {
+                        _propertyLookup.put(pn.getSimpleName(), prop);
+                    }
+                }
+            }
+        }
         for (int i = 0; i < len; ++i) {
             SettableBeanProperty prop = creatorProps[i];
             _allProperties[i] = prop;
@@ -74,23 +87,61 @@
     }
 
     /**
-     * Factory method used for building actual instances: resolves deserializers
-     * and checks for "null values".
+     * Factory method used for building actual instances to be used with POJOS:
+     * resolves deserializers, checks for "null values".
+     *
+     * @since 2.9
      */
     public static PropertyBasedCreator construct(DeserializationContext ctxt,
-            ValueInstantiator valueInstantiator, SettableBeanProperty[] srcProps)
+            ValueInstantiator valueInstantiator, SettableBeanProperty[] srcCreatorProps,
+            BeanPropertyMap allProperties)
         throws JsonMappingException
     {
-        final int len = srcProps.length;
+        final int len = srcCreatorProps.length;
         SettableBeanProperty[] creatorProps = new SettableBeanProperty[len];
         for (int i = 0; i < len; ++i) {
-            SettableBeanProperty prop = srcProps[i];
+            SettableBeanProperty prop = srcCreatorProps[i];
             if (!prop.hasValueDeserializer()) {
                 prop = prop.withValueDeserializer(ctxt.findContextualValueDeserializer(prop.getType(), prop));
             }
             creatorProps[i] = prop;
         }
-        return new PropertyBasedCreator(valueInstantiator, creatorProps,
+        return new PropertyBasedCreator(ctxt, valueInstantiator, creatorProps,
+                allProperties.isCaseInsensitive(),
+                allProperties.hasAliases());
+    }
+
+    /**
+     * Factory method used for building actual instances to be used with types
+     * OTHER than POJOs.
+     * resolves deserializers and checks for "null values".
+     *
+     * @since 2.9
+     */
+    public static PropertyBasedCreator construct(DeserializationContext ctxt,
+            ValueInstantiator valueInstantiator, SettableBeanProperty[] srcCreatorProps,
+            boolean caseInsensitive)
+        throws JsonMappingException
+    {
+        final int len = srcCreatorProps.length;
+        SettableBeanProperty[] creatorProps = new SettableBeanProperty[len];
+        for (int i = 0; i < len; ++i) {
+            SettableBeanProperty prop = srcCreatorProps[i];
+            if (!prop.hasValueDeserializer()) {
+                prop = prop.withValueDeserializer(ctxt.findContextualValueDeserializer(prop.getType(), prop));
+            }
+            creatorProps[i] = prop;
+        }
+        return new PropertyBasedCreator(ctxt, valueInstantiator, creatorProps, 
+                caseInsensitive, false);
+    }
+
+    @Deprecated // since 2.9
+    public static PropertyBasedCreator construct(DeserializationContext ctxt,
+            ValueInstantiator valueInstantiator, SettableBeanProperty[] srcCreatorProps)
+        throws JsonMappingException
+    {
+        return construct(ctxt, valueInstantiator, srcCreatorProps,
                 ctxt.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES));
     }
 
@@ -158,7 +209,7 @@
 
     /**
      * Simple override of standard {@link java.util.HashMap} to support
-     * case-insensitive access to creator properties.
+     * case-insensitive access to creator properties
      *
      * @since 2.8.5
      */
@@ -168,8 +219,7 @@
 
         @Override
         public SettableBeanProperty get(Object key0) {
-            String key = (String) key0;
-            return super.get(key.toLowerCase());
+            return super.get(((String) key0).toLowerCase());
         }
 
         @Override
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/FactoryBasedEnumDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/FactoryBasedEnumDeserializer.java
index 7ec85e2..f1e0fbd 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/FactoryBasedEnumDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/FactoryBasedEnumDeserializer.java
@@ -13,6 +13,7 @@
 import com.fasterxml.jackson.databind.JavaType;
 import com.fasterxml.jackson.databind.JsonDeserializer;
 import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.MapperFeature;
 import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
 import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
 import com.fasterxml.jackson.databind.deser.ValueInstantiator;
@@ -119,7 +120,8 @@
                 value = p.getText();
             } else if ((_creatorProps != null) && p.isExpectedStartObjectToken()) {
                 if (_propCreator == null) {
-                    _propCreator = PropertyBasedCreator.construct(ctxt, _valueInstantiator, _creatorProps);
+                    _propCreator = PropertyBasedCreator.construct(ctxt, _valueInstantiator, _creatorProps,
+                            ctxt.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES));
                 }
                 p.nextToken();
                 return deserializeEnumUsingPropertyBased(p, ctxt, _propCreator);
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/MapDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/MapDeserializer.java
index 4c140a6..d4c6120 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/MapDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/MapDeserializer.java
@@ -232,7 +232,8 @@
         }
         if (_valueInstantiator.canCreateFromObjectWith()) {
             SettableBeanProperty[] creatorProps = _valueInstantiator.getFromObjectArguments(ctxt.getConfig());
-            _propertyBasedCreator = PropertyBasedCreator.construct(ctxt, _valueInstantiator, creatorProps);
+            _propertyBasedCreator = PropertyBasedCreator.construct(ctxt, _valueInstantiator, creatorProps,
+                    ctxt.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES));
         }
         _standardStringKey = _isStdKeyDeser(_containerType, _keyDeserializer);
     }
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/ConcreteBeanPropertyBase.java b/src/main/java/com/fasterxml/jackson/databind/introspect/ConcreteBeanPropertyBase.java
index f2945a7..b4d91e9 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/ConcreteBeanPropertyBase.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/ConcreteBeanPropertyBase.java
@@ -1,10 +1,14 @@
 package com.fasterxml.jackson.databind.introspect;
 
+import java.util.Collections;
+import java.util.List;
+
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.databind.AnnotationIntrospector;
 import com.fasterxml.jackson.databind.BeanProperty;
 import com.fasterxml.jackson.databind.PropertyMetadata;
+import com.fasterxml.jackson.databind.PropertyName;
 import com.fasterxml.jackson.databind.cfg.MapperConfig;
 
 /**
@@ -32,6 +36,11 @@
      */
     protected transient JsonFormat.Value _propertyFormat;
 
+    /**
+     * @since 2.9
+     */
+    protected transient List<PropertyName> _aliases;
+
     protected ConcreteBeanPropertyBase(PropertyMetadata md) {
         _metadata = (md == null) ? PropertyMetadata.STD_REQUIRED_OR_OPTIONAL : md;
     }
@@ -107,4 +116,21 @@
         }
         return v0.withOverrides(v);
     }
+
+    @Override
+    public List<PropertyName> findAliases(MapperConfig<?> config)
+    {
+        List<PropertyName> aliases = _aliases;
+        if (aliases == null) {
+            AnnotationIntrospector intr = config.getAnnotationIntrospector();
+            if (intr != null) {
+                aliases = intr.findPropertyAliases(getMember());
+            }
+            if (aliases == null) {
+                aliases = Collections.emptyList();
+            }
+            _aliases = aliases;
+        }
+        return aliases;
+    }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/PropertyAliasTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/PropertyAliasTest.java
index 307e15c..f6df4f8 100644
--- a/src/test/java/com/fasterxml/jackson/databind/deser/PropertyAliasTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/PropertyAliasTest.java
@@ -33,22 +33,27 @@
     // [databind#1029]
     public void testSimpleAliases() throws Exception
     {
-        AliasBean bean = MAPPER.readValue(aposToQuotes("{'Name':'Foobar','a':3,'xyz':37}"),
+        AliasBean bean;
+
+        // first, one indicated by field annotation, set via field
+        bean = MAPPER.readValue(aposToQuotes("{'Name':'Foobar','a':3,'xyz':37}"),
                 AliasBean.class);
         assertEquals("Foobar", bean.name);
         assertEquals(3, bean._a);
         assertEquals(37, bean._xyz);
 
-        bean = MAPPER.readValue(aposToQuotes("{'name':'Foobar','A':3,'xyz':37}"),
-                AliasBean.class);
-        assertEquals("Foobar", bean.name);
-        assertEquals(3, bean._a);
-        assertEquals(37, bean._xyz);
-
+        // then method-bound one
         bean = MAPPER.readValue(aposToQuotes("{'name':'Foobar','a':3,'Xyz':37}"),
                 AliasBean.class);
         assertEquals("Foobar", bean.name);
         assertEquals(3, bean._a);
         assertEquals(37, bean._xyz);
+        
+        // and finally, constructor-backed one
+        bean = MAPPER.readValue(aposToQuotes("{'name':'Foobar','A':3,'xyz':37}"),
+                AliasBean.class);
+        assertEquals("Foobar", bean.name);
+        assertEquals(3, bean._a);
+        assertEquals(37, bean._xyz);
     }
 }
diff --git a/src/test/java/com/fasterxml/jackson/databind/misc/BeanPropertyMapTest.java b/src/test/java/com/fasterxml/jackson/databind/misc/BeanPropertyMapTest.java
index eafa286..2240d5a 100644
--- a/src/test/java/com/fasterxml/jackson/databind/misc/BeanPropertyMapTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/misc/BeanPropertyMapTest.java
@@ -31,7 +31,8 @@
         PropertyMetadata md = PropertyMetadata.STD_REQUIRED;
         props.add(new ObjectIdValueProperty(new MyObjectIdReader("pk"), md));
         props.add(new ObjectIdValueProperty(new MyObjectIdReader("firstName"), md));
-        BeanPropertyMap propMap = new BeanPropertyMap(false, props);
+        BeanPropertyMap propMap = new BeanPropertyMap(false, props,
+                new HashMap<String,List<PropertyName>>());
         propMap = propMap.withProperty(new ObjectIdValueProperty(new MyObjectIdReader("@id"), md));
         assertNotNull(propMap);
     }