/// <summary> /// Determina si de acuerdo a los criterios de la especificación de XBRL el arco actual es una relación equivalente al arco a comparar /// Un arco es equivalente a otro si relaciona los mismos elementos y si /// tiene los mismos atributos no excentos de comparación y /// para cada atriburo no excento de comparación hay un atributo equivalente de la misma estructura (s-equal) en el arco a comparar /// Atributos: uso y prioridad son excentos y los atributos de los siguientes espacios de nombres: /// http://www.w3.org/2000/xmlns/ y http://www.w3.org/1999/xlink son excentos, cualquier otro atributro es no-excento /// y los fragmentos de xml de los lados "from" y "to" son identicos entre los 2 arcos /// </summary> /// <param name="arcoAComparar"></param> /// <returns></returns> public bool EsRelacionEquivalente(Arco arcoAComparar) { if (ArcoRol != null && !ArcoRol.Equals(arcoAComparar.ArcoRol)) { return(false); } return(TieneMismosDestinos(arcoAComparar) && TieneMismosAtributosNoExcentos(arcoAComparar)); }
/// <summary> /// Evalúa si el arco actual está pohibido por el arco enviado como parámetro por los criterios: /// El arco a comparar tiene como atributo de uso el valor de "prohibited" /// Es el arco actual es una relación equivalente al arco a comparar /// La prioridad del arco a comparar es mayor o igual a la prioridad de este arco /// </summary> /// <param name="arcoAComparar"></param> /// <returns></returns> public bool EstaProhibidoPor(Arco arcoAComparar) { if (TiposUso.Prohibido.Valor.Equals(arcoAComparar.Uso)) { if (arcoAComparar.Prioridad >= Prioridad && EsRelacionEquivalente(arcoAComparar)) { return(true); } } return(false); }
/// <summary> /// Procesa la definición de un arco para que se refleje en la estructura de este arbol. /// </summary> /// <param name="arco">El arco a procesar</param> public void ProcesarArcoRecursivo(Arco arco) { //Para cada elemento origen foreach (ElementoLocalizable elementoDesde in arco.ElementoDesde) { if (elementoDesde.Destino == null) { continue; } NodoLinkbase nodoOrigen = null; IList <NodoLinkbase> recorridoOrigen = new List <NodoLinkbase>(); if (elementoDesde.Destino.Id != null) { //empezar a buscar el nodo destino empezando por el raiz considerando el rol nodoOrigen = BuscarNodo(NodoRaiz, elementoDesde.Destino.Id, arco.ArcoRol, recorridoOrigen); } if (nodoOrigen == null) { nodoOrigen = CrearNodo(elementoDesde); } //Para cada nodo destino foreach (ElementoLocalizable elementoHacia in arco.ElementoHacia) { NodoLinkbase nodoDestino = null; IList <NodoLinkbase> recorridoDestino = new List <NodoLinkbase>(); if (elementoHacia.Destino.Id != null) { //empezar a buscar el nodo destino empezando por el raiz considerando el rol nodoDestino = BuscarNodo(NodoRaiz, elementoHacia.Destino.Id, arco.ArcoRol, recorridoDestino); } if (nodoDestino == null) { nodoDestino = CrearNodo(elementoHacia); } AgregarArco(arco, nodoOrigen, nodoDestino); //Verificar si el arco origen no tiene padres, se debe relacionar con el nodo raíz if (!nodoOrigen.TienePadres()) { AgregarArco(null, NodoRaiz, nodoOrigen); } //Si el nodo destino tiene como padre al raíz y no participó en un ciclo entonces quitar su relación if (nodoDestino.EsNodoPadre(NodoRaiz) && !recorridoOrigen.Contains(nodoDestino)) { QuitarArco(NodoRaiz, nodoDestino); } } } }
/// <summary> /// Crea una relación entre los nodos origen y destino de acuerdo al arco mandado como parámetro. /// /// </summary> /// <param name="arco">El arco que representa la relación, si el arco es nulo entonces es una relación /// no especificada entre 2 nodos, por ejemplo, entre el nodo raíz y el primer hijo</param> /// <param name="nodoOrigen">Origen de la relación</param> /// <param name="nodoDestino">Destino de la relación</param> /// <param name="rol">Rol destino del arco, en caso de que sea importado de otro linkbase rol (caso dimensional)</param> private void AgregarArco(Arco arco, NodoLinkbase nodoOrigen, NodoLinkbase nodoDestino, string rol = "") { //Relación entrante del destino var conectorEntrante = new ConectorLinkbase(arco, nodoOrigen); nodoDestino.ConectoresEntrantes.Add(conectorEntrante); //Relación saliente del origen var conectorSaliente = new ConectorLinkbase(arco, nodoDestino); if (!String.IsNullOrEmpty(rol)) { conectorSaliente.RolOrigen = rol; } nodoOrigen.ConectoresSalientes.Add(conectorSaliente); }
/// <summary> /// Evalúa si este arco tiene los mismos elementos de origen y destino del arco a comparar /// </summary> /// <param name="arcoAComparar"></param> /// <returns></returns> private bool TieneMismosDestinos(Arco arcoAComparar) { if (this == arcoAComparar) { return(true); } //Comparar todos los elementos desde int elementosIguales = 0; foreach (ElementoLocalizable elemento in ElementoDesde) { //Encontrar el mismo elemento en el arco a comparar foreach (ElementoLocalizable elementoAComparar in arcoAComparar.ElementoDesde) { if (elemento.Destino == elementoAComparar.Destino) { elementosIguales++; break; } } } if (elementosIguales != ElementoDesde.Count) { return(false); } //Comparar todos los elementos hacia elementosIguales = 0; foreach (ElementoLocalizable elemento in ElementoHacia) { //Encontrar el mismo elemento en el arco a comparar foreach (ElementoLocalizable elementoAComparar in arcoAComparar.ElementoHacia) { if (elemento.Destino == elementoAComparar.Destino) { elementosIguales++; break; } } } if (elementosIguales != ElementoDesde.Count) { return(false); } return(true); }
/// <summary> /// Evalúa si este arco es reemplazado por el arco a comparar enviado como parámetro /// El arco a comparar tiene como atributo de uso el valor de "optional" /// El arco actual es una relación equivalente al arco a comparar /// La prioridad del arco a comparar es mayor o igual a la prioridad de este arco /// </summary> /// <param name="arcoAComparar">Arco a verificar si reemplaza al actual</param> /// <returns></returns> public bool EsReemplazadoPor(Arco arcoAComparar) { return(arcoAComparar.Prioridad >= Prioridad && EsRelacionEquivalente(arcoAComparar)); }
/// <summary> /// Verifica que los atributos no excentos de un arco sean iguales a los atributos no excentos del otro arco: /// Atributos excentos: /// uso y prioridad y los atributos cuyo namespace sea: /// http://www.w3.org/2000/xmlns/ y http://www.w3.org/1999/xlink /// </summary> /// <param name="arcoAComparar"></param> /// <returns></returns> private bool TieneMismosAtributosNoExcentos(Arco arcoAComparar) { //Extraer atriburos no excenteos de ambos - se omite el atriburo de orden con el fin de compararlo manualmente IList <XmlAttribute> atributosNoExcentos = new List <XmlAttribute>(); IList <XmlAttribute> atributosNoExcentosComparar = new List <XmlAttribute>(); foreach (XmlAttribute attr in ElementoXML.Attributes) { if (!(EtiquetasXBRLConstantes.UseAttribute.Equals(attr.LocalName) || EtiquetasXBRLConstantes.PriorityAttribute.Equals(attr.LocalName) || EtiquetasXBRLConstantes.OrderAttribute.Equals(attr.LocalName) || EspacioNombresConstantes.XLinkNamespace.Equals(attr.NamespaceURI) || EspacioNombresConstantes.NamespaceAttriburosExcentos.Equals(attr.NamespaceURI))) { atributosNoExcentos.Add(attr); } } foreach (XmlAttribute attr in arcoAComparar.ElementoXML.Attributes) { if (!(EtiquetasXBRLConstantes.UseAttribute.Equals(attr.LocalName) || EtiquetasXBRLConstantes.PriorityAttribute.Equals(attr.LocalName) || EtiquetasXBRLConstantes.OrderAttribute.Equals(attr.LocalName) || EspacioNombresConstantes.XLinkNamespace.Equals(attr.NamespaceURI) || EspacioNombresConstantes.NamespaceAttriburosExcentos.Equals(attr.NamespaceURI))) { atributosNoExcentosComparar.Add(attr); } } if (atributosNoExcentos.Count != atributosNoExcentosComparar.Count) { return(false); } //buscar si hacen match los atributos foreach (XmlAttribute attr in atributosNoExcentos) { string valorNormalizado = XmlUtil.NormalizarValorNumerico(attr); bool attrIgual = false; foreach (XmlAttribute attrComparar in atributosNoExcentosComparar) { if (attr.NamespaceURI.Equals(attrComparar.NamespaceURI) && attr.LocalName.Equals(attrComparar.LocalName)) { string valorNormalizadoAcomparar = XmlUtil.NormalizarValorNumerico(attrComparar); if (!valorNormalizado.Equals(valorNormalizadoAcomparar)) { return(false); } else { attrIgual = true; break; } } } if (!attrIgual) { return(false); } } //finalmente comparar el orden return(Orden == arcoAComparar.Orden); }
/// <summary> /// Importa los arcos que han sido recuperado de otro role y los agrega a las relaciones del árbol únicamente a partir /// del conector inicial enviado como parámetro. /// Todos los arcos generan nuevos nodos para no interferir con las relaciones ya existentes /// </summary> /// <param name="conector">Conector inicial</param> /// <param name="arcosConsecutivos">Arcos a crear</param> public void ImportarArcos(ConectorLinkbase conector, IDictionary <Arco, String> arcosConsecutivos) { var nodosCreados = new List <NodoLinkbase>(); nodosCreados.Add(conector.NodoSiguiente); //Si el nodo siguiente del conector no está en el inventario, agregar if (!IndicePorId.Values.Contains(conector.NodoSiguiente)) { if (IndicePorId.ContainsKey(conector.NodoSiguiente.Elemento.Id)) { String id = "E" + Guid.NewGuid().ToString(); IndicePorId.Add(conector.NodoSiguiente.Elemento.Id + "_" + id, conector.NodoSiguiente); } } var arcosPendientes = arcosConsecutivos.ToDictionary(keyVal => keyVal.Key, keyVal => keyVal.Value); while (arcosPendientes.Count > 0) { Arco arcoUsado = null; //buscar en los arcos pendientes el enlace entre el nodo siguiente y el arco foreach (var arcoActual in arcosPendientes) { //para este arco, buscar que nodo le corresponde foreach (var elementoDesde in arcoActual.Key.ElementoDesde) { var nodoOrigenArco = nodosCreados.FirstOrDefault(x => x.Elemento == elementoDesde.Destino); if (nodoOrigenArco != null) { //Nodo localizado, crear arco foreach (var elementoHacia in arcoActual.Key.ElementoHacia) { NodoLinkbase nodoSiguiente = new NodoLinkbase(); nodosCreados.Add(nodoSiguiente); nodoSiguiente.Elemento = elementoHacia.Destino; nodoSiguiente.ConectoresEntrantes = new List <ConectorLinkbase>(); nodoSiguiente.ConectoresSalientes = new List <ConectorLinkbase>(); //Si el nodo creado corresponde a un concepto entonces agregarlo al índice con su ID, de lo contrario, generar uno temporal if (elementoHacia.Destino is ConceptTuple || elementoHacia.Destino is ConceptItem) { if (IndicePorId.ContainsKey(elementoHacia.Destino.Id)) { String id = "E" + Guid.NewGuid().ToString(); IndicePorId.Add(elementoHacia.Destino.Id + "_" + id, nodoSiguiente); } else { IndicePorId.Add(elementoHacia.Destino.Id, nodoSiguiente); } } else { String id = "E" + Guid.NewGuid().ToString(); nodoSiguiente.Elemento.Id = id; IndicePorId.Add(id, nodoSiguiente); } AgregarArco(arcoActual.Key, nodoOrigenArco, nodoSiguiente, arcoActual.Value); arcoUsado = arcoActual.Key; } } } if (arcoUsado != null) { break; } } if (arcoUsado != null) { arcosPendientes.Remove(arcoUsado); } } //Verificar los nodos que no tienen elementos entrantes, apuntarlos al raíz VerificarElementosHuerfanos(); }
/// <summary> /// Constructor completo /// </summary> /// <param name="_arco">Arco por el cuál se crea el conector</param> /// <param name="_nodoSiguente">Nodo al que apunta el contector</param> public ConectorLinkbase(Arco _arco, NodoLinkbase _nodoSiguente) { Arco = _arco; NodoSiguiente = _nodoSiguente; }