/// <summary> /// Recorre el arbol de nodos buscando un nodo con un elemento que contenga el ID buscado, /// el recorrido se realiza considerando el arco rol de la relación y se van agregando los nodos visitados /// a la lista del recorrido, se detiene la busqueda al: /// encontrar el nodo buscado /// termina el recorrido del arbol /// se encuentra un ciclo en la rama /// </summary> /// <param name="NodoActual">Nodo actualmente visitado</param> /// <param name="IDBuscado">IDentificador buscado</param> /// <param name="ArcoRol">ArcoRol a considerar</param> /// <param name="recorrido">Lista donde se agregan los nodos visitados</param> /// <returns>Nodo encontrado, null si no se localiza el nodo</returns> private NodoLinkbase BuscarNodo(NodoLinkbase NodoActual, string IDBuscado, string ArcoRol, IList <NodoLinkbase> recorrido) { //Nodo buscado if (NodoActual.Elemento.Id.Equals(IDBuscado)) { return(NodoActual); } //Termina esta rama si hay ciclos if (recorrido.Contains(NodoActual)) { return(null); } recorrido.Add(NodoActual); //Evaluar relaciones foreach (ConectorLinkbase conectorNodo in NodoActual.ConectoresSalientes.Where(con => con.Arco == null || con.Arco.ArcoRol.Equals(ArcoRol))) { NodoLinkbase nodoEncontrado = BuscarNodo(conectorNodo.NodoSiguiente, IDBuscado, ArcoRol, recorrido); if (nodoEncontrado != null) { return(nodoEncontrado); } } recorrido.Remove(NodoActual); return(null); }
/// <summary> /// Obtiene en forma de lista el conjunto de nodos que participan en el arbol de relaciones que inicia con el nodo orgien considerando /// los arcos roles enviados como parámetro. /// El nodo origen no se considera parte de la lista resultante /// </summary> /// <param name="nodoOrigen">Nodo origen de búsqueda</param> /// <param name="arcosAConsiderar">Arcos a considerar</param> /// <returns></returns> public static IList <NodoLinkbase> ObtenerListaNodosEnRelacion(NodoLinkbase nodoOrigen, IList <string> arcosAConsiderar) { IList <NodoLinkbase> resultado = new List <NodoLinkbase>(); BuscarListarNodo(nodoOrigen, arcosAConsiderar, resultado); return(resultado); }
/// <summary> /// Valida si este grafo contiene ciclos dirigidos o no dirigidos. /// </summary> /// <param name="nodo">el nodo a partir del cual se comenzará la validación.</param> /// <param name="arcoRol">El tipo de arco rol por el que se deben de filtrar las relaciones a evaluar</param> /// <returns><code>true</code> si contiene al menos un ciclo dirigido o no dirigido. <code>false</code> en cualquier otro caso.</returns> public bool ContieneCiclos(NodoLinkbase nodo, List <string> arcoRol) { bool tieneCiclos = false; if (!NodosVisitados.Contains(nodo.Elemento)) { NodosVisitados.Add(nodo.Elemento); //Se verifica si el conector que lleva al siguiente nodo es válido, es parte de los roles que se están evaluando //y si el arco es usable en la especificación de dimensiones foreach (ConectorLinkbase conector in nodo.ConectoresSalientes.Where(con => con.Arco == null || arcoRol.Contains(con.Arco.ArcoRol))) { if (conector.Arco is ArcoDefinicion && (conector.Arco as ArcoDefinicion).Usable != null && !(conector.Arco as ArcoDefinicion).Usable.Value) { continue; } tieneCiclos = ContieneCiclos(conector.NodoSiguiente, arcoRol); if (tieneCiclos) { break; } } } else { tieneCiclos = true; } return(tieneCiclos); }
/// <summary> /// Crea un elemento de nodo en base al elemento enviado como parámetro /// </summary> /// <param name="elementoDesde">Elemento origen para crear el nodo</param> /// <returns>El nodo creado</returns> private NodoLinkbase CrearNodo(ElementoLocalizable elementoDesde) { //Hacer una segunda inspección por ID para evitar que no se haya encontrado un nodo buscando por arco rol if (elementoDesde.Destino.Id != null && IndicePorId.ContainsKey(elementoDesde.Destino.Id)) { return(IndicePorId[elementoDesde.Destino.Id]); } var nodoFinal = new NodoLinkbase(); nodoFinal.Elemento = elementoDesde.Destino; nodoFinal.ConectoresEntrantes = new List <ConectorLinkbase>(); nodoFinal.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 ((elementoDesde.Destino is ConceptTuple || elementoDesde.Destino is ConceptItem) && elementoDesde.Destino.Id != null) { IndicePorId.Add(elementoDesde.Destino.Id, nodoFinal); } else { String id = "E" + Guid.NewGuid().ToString(); if (nodoFinal.Elemento.Id == null) { nodoFinal.Elemento.Id = id; } IndicePorId.Add(id, nodoFinal); } _listaNodos.Add(nodoFinal); return(nodoFinal); }
/// <summary> /// Constructor de la clase <code>ArbolLinkbase</code> /// </summary> /// <param name="rol">el rol que da origen a este árbol de linkbase</param> public ArbolLinkbase(RoleType rol) { NodoRaiz = new NodoLinkbase(); NodoRaiz.Elemento = rol; IndicePorId = new Dictionary <string, NodoLinkbase>(); NodoRaiz.ConectoresEntrantes = new List <ConectorLinkbase>(); NodoRaiz.ConectoresSalientes = new List <ConectorLinkbase>(); _listaNodos = new List <NodoLinkbase>(); }
private static void BuscarListarNodo(NodoLinkbase nodoOrigen, IList <string> arcosAConsiderar, IList <NodoLinkbase> resultado) { foreach (var conectorSiguiente in nodoOrigen.ConectoresSalientes.Where(x => x.Arco != null && arcosAConsiderar.Contains(x.Arco.ArcoRol))) { if (!resultado.Contains(conectorSiguiente.NodoSiguiente)) { resultado.Add(conectorSiguiente.NodoSiguiente); BuscarListarNodo(conectorSiguiente.NodoSiguiente, arcosAConsiderar, resultado); } } }
/// <summary> /// Elimina los conectores de relación que hay entre los 2 nodos /// </summary> /// <param name="nodoOrigen">Nodo origen de las relaciones a borrar</param> /// <param name="nodoDestino">Nodo destino de las relaciones a borrar</param> private void QuitarArco(NodoLinkbase nodoOrigen, NodoLinkbase nodoDestino) { //Para cada conector saliente del nodo origen IList <ConectorLinkbase> conectoresOrigenDestino = nodoOrigen.ConectoresSalientes.Where(conector => conector.NodoSiguiente == nodoDestino).ToList(); nodoOrigen.ConectoresSalientes = nodoOrigen.ConectoresSalientes.Except(conectoresOrigenDestino).ToList(); //Por cada conector entrante del destino conectoresOrigenDestino = nodoDestino.ConectoresEntrantes.Where(conector => conector.NodoSiguiente == nodoOrigen).ToList(); nodoDestino.ConectoresEntrantes = nodoDestino.ConectoresEntrantes.Except(conectoresOrigenDestino).ToList(); }
/// <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> /// Evalúa si el nodo tiene conectores entrantes o salientes donde participe en una relación /// con el arco rol enviado como parámetro /// </summary> /// <param name="nod">Nodo a evaluar</param> /// <returns>True si participa en alguna relación con el arco rol enviado, false en otro caso</returns> private bool ParticipaEnArcoRol(NodoLinkbase nod, List <string> arcoRol) { //Conectores salientes if (nod.ConectoresSalientes != null && nod.ConectoresSalientes.Count(con => con.Arco != null && arcoRol.Contains(con.Arco.ArcoRol)) > 0) { return(true); } if (nod.ConectoresEntrantes != null && nod.ConectoresEntrantes.Count(con => con.Arco != null && arcoRol.Contains(con.Arco.ArcoRol)) > 0) { return(true); } return(false); }
/// <summary> /// Itera los nodos hijos del nodo pasado como parámetro en busca de ciclos dirigidos como parte del algorimo de Tarjan. /// </summary> /// <param name="nodo">el nodo que debe ser consultado</param> /// <param name="arcoRol">URI del arco rol a considerar para evaluar los ciclos</param> private void BuscarElementosFuertementeConectados(NodoLinkbase nodo, List <string> arcoRol) { bool cicloASiMismo = false; nodo.Indice = Indice; nodo.IndiceBajo = Indice; Indice++; PilaNodosVisitados.Push(nodo); if (nodo.ConectoresSalientes != null && nodo.ConectoresSalientes.Count > 0) { //Evaluar relaciones representadas por arcos con el rol enviado como parámetro foreach (ConectorLinkbase conector in nodo.ConectoresSalientes.Where(con => con.Arco == null || arcoRol.Contains(con.Arco.ArcoRol))) { if (conector.NodoSiguiente.Indice < 0) { BuscarElementosFuertementeConectados(conector.NodoSiguiente, arcoRol); nodo.IndiceBajo = Math.Min(nodo.IndiceBajo, conector.NodoSiguiente.IndiceBajo); } else if (PilaNodosVisitados.Contains(conector.NodoSiguiente)) { nodo.IndiceBajo = Math.Min(nodo.IndiceBajo, conector.NodoSiguiente.IndiceBajo); if (conector.NodoSiguiente.Equals(nodo)) { cicloASiMismo = true; } } } } if (nodo.IndiceBajo == nodo.Indice) { IList <NodoLinkbase> ComponenteFuertementeConectado = new List <NodoLinkbase>(); NodoLinkbase otroNodo; do { otroNodo = PilaNodosVisitados.Pop(); if (ComponenteFuertementeConectado.Count > 0 || !otroNodo.Equals(nodo) || cicloASiMismo) { ComponenteFuertementeConectado.Add(otroNodo); } }while (nodo != otroNodo); if (ComponenteFuertementeConectado.Count > 0) { ComponentesConectadosFuertemente.Add(ComponenteFuertementeConectado); } } }
/// <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); }
public void ProcesarArcosSecuenciales(IList <Arco> arcos) { var nodosPorElemento = new Dictionary <ElementoXBRL, NodoLinkbase>(); foreach (var arco in arcos) { //Para cada elemento origen foreach (ElementoLocalizable elementoDesde in arco.ElementoDesde) { if (elementoDesde.Destino == null) { continue; } NodoLinkbase nodoOrigen = null; if (nodosPorElemento.ContainsKey(elementoDesde.Destino)) { nodoOrigen = nodosPorElemento[elementoDesde.Destino]; } else { nodoOrigen = CrearNodo(elementoDesde); nodosPorElemento.Add(nodoOrigen.Elemento, nodoOrigen); } //Para cada nodo destino foreach (ElementoLocalizable elementoHacia in arco.ElementoHacia) { if (elementoHacia.Destino == null) { continue; } NodoLinkbase nodoDestino = null; if (nodosPorElemento.ContainsKey(elementoHacia.Destino)) { nodoDestino = nodosPorElemento[elementoHacia.Destino]; } else { nodoDestino = CrearNodo(elementoHacia); nodosPorElemento.Add(nodoDestino.Elemento, nodoDestino); } AgregarArco(arco, nodoOrigen, nodoDestino); } } } AjustarNodosHuerfanos(); }
/// <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> /// Verifica si el nodo enviado como parámetro es padre de este nodo /// en alguna de sus relaciones entrantes /// </summary> /// <param name="nodoPadre">Nodo a verificar si es padre</param> /// <returns>True si es nodo padre, false en otro caso</returns> public bool EsNodoPadre(NodoLinkbase nodoPadre) { return(ConectoresEntrantes != null && ConectoresEntrantes.Count(cx => cx.NodoSiguiente == nodoPadre) > 0); }
/// <summary> /// Crea un nuevo nodo copiando los elementos relevantes del nodo enviado /// </summary> /// <param name="nodoLinkbase"></param> public NodoLinkbase(NodoLinkbase nodoLinkbase) { ConectoresEntrantes = new List <ConectorLinkbase>(); ConectoresSalientes = new List <ConectorLinkbase>(); Elemento = nodoLinkbase.Elemento; }
/// <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; }