/*
 * Copyright (C) 2013 salesforce.com, inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.auraframework.impl.root.component;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.auraframework.builder.ModuleDefRefBuilder;
import org.auraframework.def.AttributeDef;
import org.auraframework.def.AttributeDefRef;
import org.auraframework.def.DefDescriptor;
import org.auraframework.def.DefDescriptor.DefType;
import org.auraframework.def.DefinitionReference;
import org.auraframework.def.module.ModuleDef;
import org.auraframework.def.module.ModuleDefRef;
import org.auraframework.impl.root.DefinitionReferenceImpl;
import org.auraframework.throwable.quickfix.DefinitionNotFoundException;
import org.auraframework.throwable.quickfix.QuickFixException;
import org.auraframework.util.json.Json;
import org.auraframework.validation.ReferenceValidationContext;

import com.google.common.collect.Lists;

/**
 * ModuleDefRef implementation.
 * Uses same base impl as ComponentDefRef
 */
public class ModuleDefRefImpl extends DefinitionReferenceImpl<ModuleDef> implements ModuleDefRef {

    private static final long serialVersionUID = 2121381558446216947L;
    private transient DefDescriptor<?> reference;

    protected ModuleDefRefImpl(Builder builder) {
        super(builder);
    }

    @Override
    public void serialize(Json json) throws IOException {
        json.writeMapBegin();
        json.writeMapKey("componentDef");

        json.writeMapBegin();
        json.writeMapEntry("descriptor", reference);
        json.writeMapEntry("type", "module");
        json.writeMapEnd();

        json.writeMapEntry("localId", localId);

        if (!attributeValues.isEmpty()) {
            json.writeMapKey("attributes");

            json.writeMapBegin();
            json.writeMapKey("values");

            json.writeMapBegin();
            for (Map.Entry<DefDescriptor<AttributeDef>, AttributeDefRef> entry : attributeValues.entrySet()) {
                json.writeMapEntry(entry.getKey(), entry.getValue());
            }
            json.writeMapEnd();

            json.writeMapEnd();
        }

        json.writeMapEnd();
    }
    
    @Override
    public void appendDependencies(Set<DefDescriptor<?>> dependencies) {
        super.appendDependencies(dependencies);
    }

    @Override
    public void validateReferences(ReferenceValidationContext validationContext) throws QuickFixException {
        ModuleDef def = validationContext.getAccessibleDefinition(descriptor);
        if (def == null) {
            // not possible
            throw new DefinitionNotFoundException(descriptor);
        }
        this.reference = def.getDescriptor();
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof ModuleDefRefImpl) {
            ModuleDefRefImpl other = (ModuleDefRefImpl) obj;
            return descriptor.equals(other.getDescriptor()) && location.equals(other.getLocation());
        }
        return false;
    }

    @Override
    public List<AttributeDefRef> getAttributeValueList() throws QuickFixException {
        return Lists.newArrayList(this.attributeValues.values());
    }

    @Override
    public DefinitionReference get() {
        return this;
    }

    @Override
    public DefType type() {
        return DefType.MODULE;
    }

    public static class Builder extends DefinitionReferenceImpl.Builder<ModuleDefRef, ModuleDef> implements
            ModuleDefRefBuilder {

        public Builder() {
            super(ModuleDef.class);
        }

        @Override
        public ModuleDefRef build() {
            return new ModuleDefRefImpl(this);
        }
    }
}

@@ -1195,16 +1195,73 @@
 ceImpl;%0A
+import org.auraframework.throwable.AuraRuntimeException;%0A
 import o
@@ -1810,16 +1810,25 @@
 ansient 
+volatile 
 DefDescr
@@ -1833,17 +1833,25 @@
 criptor%3C
-?
+ModuleDef
 %3E refere
@@ -2001,32 +2001,553 @@
 s IOException %7B%0A
+%0A        if (this.reference == null) %7B%0A            synchronized (this) %7B%0A                if (this.reference == null) %7B%0A                    try %7B%0A                        // get correct cased descriptor from definition%0A                        ModuleDef def = this.descriptor.getDef();%0A                        this.reference = def.getDescriptor();%0A                    %7D catch (QuickFixException e) %7B%0A                        throw new AuraRuntimeException(e);%0A                    %7D%0A                %7D%0A            %7D%0A        %7D%0A%0A
         json.wri
@@ -2674,16 +2674,21 @@
 iptor%22, 
+this.
 referenc
@@ -2804,16 +2804,21 @@
 calId%22, 
+this.
 localId)
@@ -2833,16 +2833,21 @@
    if (!
+this.
 attribut
@@ -3102,16 +3102,21 @@
 entry : 
+this.
 attribut
@@ -3331,20 +3331,16 @@
 ;%0A    %7D%0A
-    
 %0A    @Ov
@@ -3658,24 +3658,29 @@
 eDefinition(
+this.
 descriptor);
@@ -3785,16 +3785,21 @@
 ception(
+this.
 descript
@@ -4047,16 +4047,21 @@
  return 
+this.
 descript
@@ -4096,16 +4096,21 @@
 r()) && 
+this.
 location

Set ModuleDefRefImpl reference descriptor for serialization (#2853)