private function normalizeSpecification(array $input)
    {
        $output = [];
        $mayBeChildless = null;
        
        foreach ($input as $key => $spec) {
            if ($key === 'attributes') {
                if (!is_array($spec)) {
                    throw new LogicException('Value under key '.$key.' have to be an array');
                }
                $output[$key] = [];
                foreach ($spec as $attrName => $attrSpec) {
                    if (is_string($attrName)) {
                        if (!is_array($attrSpec)) {
                            throw new LogicException('Attribute specification have to be an array');
                        }
                        
                        if (!empty($attrSpec['maxLength'])
                            && (!is_int($attrSpec['maxLength']) || $attrSpec['maxLength'] < 1)
                        ) {
                            throw new LogicException('Attribute constraint "maxLength" has to be an integer >= 1');
                        }
                        
                        if (!empty($attrSpec['enum']) && !is_array($attrSpec['enum'])) {
                            throw new LogicException('Attribute constraint "enum" has to be an array');
                        }
    
                        $output[$key][$attrName] = array_merge($this->defaultAttrSpec, $attrSpec);
                    } else {
                        $output[$key][$attrSpec] = $this->defaultAttrSpec;
                    }
                }
                
            } elseif ($key === 'children') {
                $output[$key] = [];
                foreach ($input[$key] as $childKey => $child) {
                    if (is_string($childKey)) {
                        if (!is_array($child)) {
                            throw new LogicException('Invalid child specification for key '.$childKey);
                        }
                        $output[$key][$childKey] = array_merge(
                            $this->defaultChildSpec,
                            $this->normalizeSpecification($child)
                        );
                        if ($mayBeChildless !== false) {
                            $mayBeChildless = $output[$key][$childKey]['mayBeChildless'];
                        }
                    } else {
                        $output[$key][$child] = $this->defaultChildSpec;
                        if ($mayBeChildless !== false) {
                            $mayBeChildless = $output[$key][$child]['mayBeChildless'];
                        }
                    }
                }
    
            } else {
                $output[$key] = $spec;
            }
        }
    
        if (!isset($output['mayBeChildless'])) {
            if ($mayBeChildless !== null) {
                $output['mayBeChildless'] = $mayBeChildless;
            } elseif (isset($this->defaultChildSpec['mayBeChildless'])) {
                $output['mayBeChildless'] = $this->defaultChildSpec['mayBeChildless'];
            }
        }
        
        return $output;
    }